@procaaso/alphinity-ui-components 1.16.0 → 1.17.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.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Button/Button.tsx","../src/components/ControlPanel/ControlPanel.tsx","../src/components/Selector/Selector.tsx","../src/components/ValueEntry/ValueEntry.tsx","../src/components/ValueEntry/LegacyValueEntry.tsx","../src/theme/statusColors.ts","../src/components/StatusIndicator/StatusIndicator.tsx","../src/theme/unknownStatusGlyph.tsx","../src/components/NodeControlsPanel/NodeControlsPanel.tsx","../src/components/DeviceControlPanel/DeviceControlPanel.tsx","../src/components/NumpadDialog/NumpadDialog.tsx","../src/components/layout/FullscreenWorkspace/FullscreenWorkspace.tsx","../src/components/layout/FullscreenWorkspace/FullscreenWorkspace.styles.tsx","../src/components/layout/FullscreenWorkspace/FullscreenWorkspace.overlays.tsx","../src/components/layout/FullscreenWorkspace/FullscreenWorkspace.visuals.tsx","../src/theme/ThemeContext.tsx","../src/theme/ThemeToggle.tsx","../src/components/PIDCanvas/PIDCanvas.tsx","../src/components/PIDCanvas/NodeRenderer.tsx","../src/diagram/symbols/mockSymbolLibrary.ts","../src/diagram/symbols/appearanceSchema.ts","../src/diagram/badges/nodeBadgeGlyphs.ts","../src/components/PIDCanvas/PipeRenderer.tsx","../src/components/PIDCanvas/DataOverlay.tsx","../src/components/PIDCanvas/PositionPanel.tsx","../src/diagram/hooks/useViewBox.ts","../src/diagram/hooks/useSelection.ts","../src/diagram/hooks/useTelemetry.ts","../src/diagram/hooks/useTelemetryStatus.ts","../src/diagram/hooks/useBooleanIndicators.ts","../src/diagram/hooks/useDrag.ts","../src/diagram/hooks/useKeyboardShortcuts.ts","../src/diagram/utils/portCalculations.ts","../src/components/SymbolLibrary/SymbolLibrary.tsx","../src/components/NodeConfigPanel/NodeConfigPanel.tsx","../src/diagram/utils/serialization.ts","../src/diagram/examples/exampleDiagram.ts","../src/utils/thresholds.ts","../src/diagram/schema/DeviceControlBinding.ts","../src/hooks/useDeviceControls.ts","../src/diagram/schema/DeviceConfigBinding.ts","../src/hooks/useDeviceConfigs.ts","../src/components/DeviceConfigPanel/DeviceConfigPanel.tsx","../src/diagram/hooks/useSimulation.ts"],"sourcesContent":["import React from 'react';\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: 'primary' | 'secondary';\n children: React.ReactNode;\n}\n\nexport const Button: React.FC<ButtonProps> = ({\n variant = 'primary',\n children,\n className = '',\n ...props\n}) => {\n // Use CSS classes for base and variant styles\n const baseClass = 'ui-btn';\n const variantClass = variant ? `ui-btn--${variant}` : '';\n return (\n <button className={`${baseClass} ${variantClass} ${className}`.trim()} {...props}>\n {children}\n </button>\n );\n};\n","import React, { useState } from 'react';\nimport { Selector, SelectorOption } from '../Selector';\nimport { LegacyValueEntry as ValueEntry } from '../ValueEntry';\nimport { Button } from '../Button';\nimport type { ScalingMetadata } from '../../diagram/schema/TelemetryBinding';\n\nexport interface ModeConfig {\n /** Units for this mode */\n unit?: string;\n /** Label for the primary setpoint */\n label?: string;\n /** Minimum value for primary setpoint */\n min?: number;\n /** Maximum value for primary setpoint */\n max?: number;\n /** Precision for primary setpoint */\n precision?: number;\n /** Whether this mode has a second setpoint (like ramp time) */\n hasSecondSetpoint?: boolean;\n /** Label for the second setpoint */\n secondLabel?: string;\n /** Units for the second setpoint */\n secondUnit?: string;\n /** Min value for second setpoint */\n secondMin?: number;\n /** Max value for second setpoint */\n secondMax?: number;\n /** Precision for second setpoint */\n secondPrecision?: number;\n}\n\nexport interface ControlPanelProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Panel title/identifier */\n title: string;\n /** Currently selected mode */\n selectedMode: string;\n /** Available mode options */\n modeOptions: SelectorOption[];\n /** Callback when mode changes */\n onModeChange: (mode: string) => void;\n /** Typed/target setpoint value (editable) */\n setpointValue: number | string;\n /** Callback when setpoint changes */\n onSetpointChange: (value: number | string) => void;\n /**\n * The setpoint the control loop is currently running: read-only feedback\n * from live telemetry, shown beside the typed setpoint above so an\n * operator sees target and current separately (the typed field never\n * \"snaps back\" to reflect the live value).\n *\n * Optional and hidden by default: the caller decides when a live-feedback\n * row is worth showing, since always showing a normally-differing current\n * value can read as a fault when none exists. When this tag carries a\n * maintenance force, the caller MUST pass this prop at that time - a\n * hidden value shows nothing, forced or not, but a shown value must carry\n * its FORCED mark (#3607, R19).\n */\n currentSetpointValue?: number | string;\n /**\n * True when `currentSetpointValue` is a maintenance force, not a measured\n * reading. Ignored when `currentSetpointValue` is not provided.\n */\n currentSetpointForced?: boolean;\n /**\n * The real, unforced value for `currentSetpointValue`. Optional; absent\n * until the backend publishes it (alphinity-tffi unit U3b).\n */\n currentSetpointRealValue?: number | string;\n /** Current second setpoint value (for ramp mode, etc.) */\n secondSetpointValue?: number | string;\n /** Callback when second setpoint changes */\n onSecondSetpointChange?: (value: number | string) => void;\n /** Configuration for each mode */\n modeConfigs?: Record<string, ModeConfig>;\n /** Default unit when no mode config is found */\n defaultUnit?: string;\n /** Default label when no mode config is found */\n defaultLabel?: string;\n /** Default min when no mode config is found */\n defaultMin?: number;\n /** Default max when no mode config is found */\n defaultMax?: number;\n /** Default precision when no mode config is found */\n defaultPrecision?: number;\n /**\n * Uniform scaling / EU metadata (#1663). When provided, supplies the\n * units / min / max / precision defaults from the live published scaling\n * record - used only where an explicit `default*` prop or a mode config does\n * not already set the value (modeConfig > explicit default > scaling >\n * hardcoded). Lets the consumer drive the panel from the scaling stream\n * instead of static diagram config.\n */\n scaling?: ScalingMetadata;\n /** Status state for the entire panel */\n status?: 'normal' | 'alarm' | 'warning' | 'disabled';\n /** Whether the panel is collapsed */\n collapsed?: boolean;\n /** Callback when collapse state changes */\n onCollapseChange?: (collapsed: boolean) => void;\n /** Panel size variant */\n size?: 'small' | 'medium' | 'large';\n /** Whether setpoint is read-only */\n setpointReadOnly?: boolean;\n /** Whether to show start/stop buttons */\n showStartStopButtons?: boolean;\n /** Whether the process is currently running */\n isRunning?: boolean;\n /** Callback when start button is clicked */\n onStart?: () => void;\n /** Callback when stop button is clicked */\n onStop?: () => void;\n /** Custom start button text */\n startButtonText?: string;\n /** Custom stop button text */\n stopButtonText?: string;\n}\n\nexport const ControlPanel: React.FC<ControlPanelProps> = ({\n title,\n selectedMode,\n modeOptions,\n onModeChange,\n setpointValue,\n onSetpointChange,\n currentSetpointValue,\n currentSetpointForced = false,\n currentSetpointRealValue,\n secondSetpointValue,\n onSecondSetpointChange,\n modeConfigs = {},\n defaultUnit,\n defaultLabel = 'Setpoint',\n defaultMin,\n defaultMax,\n defaultPrecision,\n scaling,\n status = 'normal',\n collapsed = false,\n onCollapseChange,\n size = 'medium',\n setpointReadOnly = false,\n showStartStopButtons = false,\n isRunning = false,\n onStart,\n onStop,\n startButtonText = 'Start',\n stopButtonText = 'Stop',\n className = '',\n ...props\n}) => {\n const [isExpanded, setIsExpanded] = useState(!collapsed);\n\n const toggleExpanded = () => {\n const newExpanded = !isExpanded;\n setIsExpanded(newExpanded);\n onCollapseChange?.(!newExpanded);\n };\n\n // Get current mode configuration\n const currentModeConfig = modeConfigs[selectedMode] || {};\n // Resolution order: mode config > explicit default prop > scaling metadata\n // (#1663) > hardcoded fallback.\n const currentUnit = currentModeConfig.unit || defaultUnit || scaling?.units || 'Units';\n const currentLabel = currentModeConfig.label || defaultLabel;\n const currentMin = currentModeConfig.min ?? defaultMin ?? scaling?.euLow ?? 0;\n const currentMax = currentModeConfig.max ?? defaultMax ?? scaling?.euHigh ?? 100;\n const currentPrecision =\n currentModeConfig.precision ?? defaultPrecision ?? scaling?.decimals ?? 2;\n const hasSecondSetpoint = currentModeConfig.hasSecondSetpoint || false;\n\n // Use CSS classes for base, status, size, and expanded state\n const baseClass = 'ui-control-panel';\n const statusClass = status ? `ui-control-panel--${status}` : '';\n const sizeClass = size ? `ui-control-panel--${size}` : '';\n const expandedClass = isExpanded ? 'is-expanded' : 'is-collapsed';\n const collapsibleClass = onCollapseChange ? 'is-collapsible' : '';\n\n return (\n <div\n className={`${baseClass} ${statusClass} ${sizeClass} ${expandedClass} ${collapsibleClass} ${className}`.trim()}\n {...props}\n >\n {/* Status Badge */}\n <div className={`ui-control-panel-badge ui-control-panel-badge--${status}`} />\n\n {/* Header */}\n <div\n className=\"ui-control-panel-header\"\n onClick={onCollapseChange ? toggleExpanded : undefined}\n >\n <h3 className=\"ui-control-panel-title\">{title}</h3>\n {onCollapseChange && (\n <svg\n className=\"ui-control-panel-chevron\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n )}\n </div>\n\n {/* Content */}\n <div className=\"ui-control-panel-content\">\n {/* Mode Selector */}\n <div>\n <Selector\n value={selectedMode}\n onChange={onModeChange}\n options={modeOptions}\n label=\"Control Mode\"\n status={status}\n size={size === 'small' ? 'small' : size === 'large' ? 'large' : 'medium'}\n disabled={status === 'disabled'}\n />\n </div>\n\n {/* Primary Setpoint Value Entry (typed/target) */}\n <div>\n <ValueEntry\n value={setpointValue}\n onChange={onSetpointChange}\n label={currentLabel}\n unit={currentUnit}\n min={currentMin}\n max={currentMax}\n precision={currentPrecision}\n status={status}\n readOnly={setpointReadOnly || status === 'disabled'}\n disabled={status === 'disabled'}\n />\n </div>\n\n {/* Current setpoint (read-only feedback). Hidden unless the caller\n passes a value - see currentSetpointValue doc comment. */}\n {currentSetpointValue !== undefined && (\n <div>\n <ValueEntry\n value={currentSetpointValue}\n onChange={() => {}}\n label={`${currentLabel} (Current)`}\n unit={currentUnit}\n precision={currentPrecision}\n status={status}\n readOnly\n forced={currentSetpointForced}\n realValue={currentSetpointRealValue}\n />\n </div>\n )}\n\n {/* Second Setpoint (for ramp mode, etc.) */}\n {hasSecondSetpoint && onSecondSetpointChange && (\n <div>\n <ValueEntry\n value={secondSetpointValue || 0}\n onChange={onSecondSetpointChange}\n label={currentModeConfig.secondLabel || 'Time'}\n unit={currentModeConfig.secondUnit || 'sec'}\n min={currentModeConfig.secondMin ?? 0}\n max={currentModeConfig.secondMax ?? 999}\n precision={currentModeConfig.secondPrecision ?? 0}\n status={status}\n readOnly={setpointReadOnly || status === 'disabled'}\n disabled={status === 'disabled'}\n />\n </div>\n )}\n\n {/* Start/Stop Buttons */}\n {showStartStopButtons && (onStart || onStop) && (\n <div className=\"ui-control-panel-buttons\">\n {onStart && (\n <Button\n variant=\"primary\"\n onClick={onStart}\n disabled={isRunning || status === 'disabled'}\n className={isRunning ? 'is-running' : ''}\n >\n {startButtonText}\n </Button>\n )}\n {onStop && (\n <Button\n variant=\"secondary\"\n onClick={onStop}\n disabled={!isRunning || status === 'disabled'}\n className={`ui-control-panel-stop-btn${!isRunning ? ' is-disabled' : ''}`}\n >\n {stopButtonText}\n </Button>\n )}\n </div>\n )}\n\n {/* Additional Info Row */}\n <div className=\"ui-control-panel-info\">\n <span>Mode: {selectedMode.toUpperCase()}</span>\n <div className=\"ui-control-panel-info-values\">\n <span>\n SP: {String(setpointValue)}\n {currentUnit}\n </span>\n {hasSecondSetpoint && secondSetpointValue && (\n <span>\n {currentModeConfig.secondLabel}: {String(secondSetpointValue)}\n {currentModeConfig.secondUnit}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n};\n","import React, { useState, useRef, useEffect } from 'react';\n\nexport interface SelectorOption {\n /** Unique value for the option */\n value: string;\n /** Display label for the option */\n label: string;\n /** Whether this option is disabled */\n disabled?: boolean;\n /** Optional description for tooltip or additional context */\n description?: string;\n}\n\nexport interface SelectorProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Currently selected value */\n value: string;\n /** Callback when selection changes */\n onChange: (value: string) => void;\n /** List of available options */\n options: SelectorOption[];\n /** Placeholder text when no option is selected */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Status state for visual feedback */\n status?: 'normal' | 'alarm' | 'warning' | 'disabled';\n /** Size variant */\n size?: 'small' | 'medium' | 'large';\n /** Label for the selector */\n label?: string;\n /** Whether the dropdown should open upward */\n dropdownUp?: boolean;\n}\n\nexport const Selector: React.FC<SelectorProps> = ({\n value,\n onChange,\n options,\n placeholder = 'Select option...',\n disabled = false,\n status = 'normal',\n size = 'medium',\n label,\n dropdownUp: _dropdownUp = false,\n className = '',\n ...props\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const [focusedIndex, setFocusedIndex] = useState(-1);\n const selectorRef = useRef<HTMLDivElement>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (selectorRef.current && !selectorRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n setFocusedIndex(-1);\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Handle keyboard navigation\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (disabled) return;\n switch (e.key) {\n case 'Enter':\n case ' ':\n e.preventDefault();\n if (!isOpen) {\n setIsOpen(true);\n setFocusedIndex(\n Math.max(\n 0,\n options.findIndex((opt) => opt.value === value)\n )\n );\n } else if (focusedIndex >= 0) {\n const selectedOption = options[focusedIndex];\n if (!selectedOption.disabled) {\n onChange(selectedOption.value);\n setIsOpen(false);\n setFocusedIndex(-1);\n }\n }\n break;\n case 'Escape':\n setIsOpen(false);\n setFocusedIndex(-1);\n break;\n case 'ArrowDown':\n e.preventDefault();\n if (!isOpen) {\n setIsOpen(true);\n setFocusedIndex(\n Math.max(\n 0,\n options.findIndex((opt) => opt.value === value)\n )\n );\n } else {\n setFocusedIndex((prev) => {\n const nextIndex = Math.min(prev + 1, options.length - 1);\n return options[nextIndex]?.disabled ? prev : nextIndex;\n });\n }\n break;\n case 'ArrowUp':\n e.preventDefault();\n if (isOpen) {\n setFocusedIndex((prev) => {\n const nextIndex = Math.max(prev - 1, 0);\n return options[nextIndex]?.disabled ? prev : nextIndex;\n });\n }\n break;\n }\n };\n\n // CSS class names for base, status, and size\n const baseClass = 'ui-selector';\n const statusClass = status ? `ui-selector--${status}` : '';\n const sizeClass = size ? `ui-selector--${size}` : '';\n\n // Get current selected option\n const selectedOption = options.find((opt) => opt.value === value);\n\n return (\n <div className={`ui-selector-container ${className}`.trim()}>\n {label && <label className=\"ui-selector-label\">{label}</label>}\n <div ref={selectorRef} style={{ position: 'relative' }}>\n <div\n className={`${baseClass} ${statusClass} ${sizeClass}`.trim()}\n onClick={() => !disabled && setIsOpen(!isOpen)}\n onKeyDown={handleKeyDown}\n tabIndex={disabled ? -1 : 0}\n role=\"combobox\"\n aria-expanded={isOpen}\n aria-haspopup=\"listbox\"\n {...props}\n >\n <span className=\"ui-selector-value\">\n {selectedOption ? selectedOption.label : placeholder}\n </span>\n {/* Arrow Icon */}\n <svg className=\"ui-selector-arrow\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n </div>\n\n {/* Dropdown */}\n {isOpen && (\n <div ref={dropdownRef} className=\"ui-selector-dropdown\" role=\"listbox\">\n {options.map((option, index) => (\n <div\n key={option.value}\n className={`ui-selector-option${option.disabled ? ' is-disabled' : ''}${option.value === value ? ' is-selected' : ''}`}\n onClick={() => {\n if (!option.disabled) {\n onChange(option.value);\n setIsOpen(false);\n setFocusedIndex(-1);\n }\n }}\n onMouseEnter={() => !option.disabled && setFocusedIndex(index)}\n role=\"option\"\n aria-selected={option.value === value}\n aria-disabled={option.disabled}\n title={option.description}\n >\n <div className=\"ui-selector-option-label\">\n <span>{option.label}</span>\n {option.value === value && (\n <svg className=\"ui-selector-check\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path\n fillRule=\"evenodd\"\n d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n clipRule=\"evenodd\"\n />\n </svg>\n )}\n </div>\n {option.description && (\n <div className=\"ui-selector-option-desc\">{option.description}</div>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n};\n","import React, { useState, useCallback } from 'react';\n\nexport interface ValueEntryProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'> {\n /** Current value */\n value: number | string;\n /** Callback when value changes */\n onChange: (value: number | string) => void;\n /** Unit of measurement to display */\n unit?: string;\n /** Status state for visual feedback */\n status?: 'normal' | 'alarm' | 'warning' | 'disabled';\n /** Minimum allowed value (for numeric inputs) */\n min?: number;\n /** Maximum allowed value (for numeric inputs) */\n max?: number;\n /** Number of decimal places (for numeric inputs) */\n precision?: number;\n /** Input type - numeric or text */\n inputType?: 'numeric' | 'text';\n /** Label for the input */\n label?: string;\n /** Whether the input is read-only */\n readOnly?: boolean;\n /**\n * True when `value` is a maintenance force, not a measured reading\n * (#3607, R19). Source: the control program's Sts_SimulationActive flag.\n * Renders a FORCED badge; never changes `status`, which stays an\n * independent fact about the same reading.\n */\n forced?: boolean;\n /**\n * The real, unforced value, shown beside `value` when `forced` is true.\n * Optional: the backend does not publish this for every tag kind yet\n * (alphinity-tffi unit U3b). When absent, only the FORCED badge renders.\n */\n realValue?: number | string;\n}\n\nexport const ValueEntry: React.FC<ValueEntryProps> = ({\n value,\n onChange,\n unit,\n status = 'normal',\n min,\n max,\n precision = 2,\n inputType = 'numeric',\n label,\n readOnly = false,\n forced = false,\n realValue,\n className = '',\n disabled,\n ...props\n}) => {\n const [displayValue, setDisplayValue] = useState(String(value));\n const [isEditing, setIsEditing] = useState(false);\n\n const handleFocus = useCallback(() => {\n setIsEditing(true);\n setDisplayValue(String(value));\n }, [value]);\n\n const handleBlur = useCallback(() => {\n setIsEditing(false);\n let newValue: number | string = displayValue;\n\n if (inputType === 'numeric') {\n const numValue = parseFloat(displayValue);\n if (!isNaN(numValue)) {\n // Apply min/max constraints\n let constrainedValue = numValue;\n if (min !== undefined) constrainedValue = Math.max(min, constrainedValue);\n if (max !== undefined) constrainedValue = Math.min(max, constrainedValue);\n\n // Apply precision\n constrainedValue = parseFloat(constrainedValue.toFixed(precision));\n newValue = constrainedValue;\n setDisplayValue(String(constrainedValue));\n } else {\n // Invalid number, revert to original value\n setDisplayValue(String(value));\n return;\n }\n }\n\n if (newValue !== value) {\n onChange(newValue);\n }\n }, [displayValue, inputType, min, max, precision, value, onChange]);\n\n const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n setDisplayValue(e.target.value);\n }, []);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.currentTarget.blur();\n }\n if (e.key === 'Escape') {\n setDisplayValue(String(value));\n e.currentTarget.blur();\n }\n },\n [value]\n );\n\n // Use CSS classes for base, status, and unit styles\n const baseClass = 'ui-value-entry';\n const unitClass = unit ? 'ui-value-entry--with-unit' : '';\n const currentStatus = disabled || readOnly ? 'disabled' : status;\n const statusVariantClass = currentStatus ? `ui-value-entry--${currentStatus}` : '';\n const forcedClass = forced ? 'ui-value-entry--forced' : '';\n const hasRealValue = forced && realValue !== undefined;\n\n const displayText = isEditing\n ? displayValue\n : inputType === 'numeric'\n ? Number(value).toFixed(precision)\n : String(value);\n\n return (\n <div className=\"ui-value-entry-container\">\n {label && (\n <label className=\"ui-value-entry-label\">\n {label}\n {forced && (\n <span className=\"ui-value-entry-forced-badge\" aria-label=\"Forced\">\n F\n </span>\n )}\n </label>\n )}\n <div className=\"ui-value-entry-wrapper\">\n <input\n type={inputType === 'numeric' ? 'text' : 'text'}\n value={displayText}\n onChange={handleChange}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n disabled={disabled}\n readOnly={readOnly}\n className={`${baseClass} ${statusVariantClass} ${forcedClass} ${unitClass} ${className}`.trim()}\n {...props}\n />\n {unit && <span className=\"ui-value-entry-unit\">{unit}</span>}\n {status === 'alarm' && <div className=\"ui-value-entry-badge ui-value-entry-badge--alarm\" />}\n {status === 'warning' && (\n <div className=\"ui-value-entry-badge ui-value-entry-badge--warning\" />\n )}\n {!label && forced && (\n <span className=\"ui-value-entry-forced-badge\" aria-label=\"Forced\">\n F\n </span>\n )}\n </div>\n {hasRealValue && (\n <div className=\"ui-value-entry-real-value\">\n REAL {String(realValue)}\n {unit ? ` ${unit}` : ''}\n </div>\n )}\n </div>\n );\n};\n","import React, { useState, useCallback } from 'react';\nimport { FORCED_MARK_COLOR } from '../../theme/statusColors';\n\nexport interface LegacyValueEntryProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'> {\n /** Current value */\n value: number | string;\n /** Callback when value changes */\n onChange: (value: number | string) => void;\n /** Unit of measurement to display */\n unit?: string;\n /** Status state for visual feedback */\n status?: 'normal' | 'alarm' | 'warning' | 'disabled';\n /** Minimum allowed value (for numeric inputs) */\n min?: number;\n /** Maximum allowed value (for numeric inputs) */\n max?: number;\n /** Number of decimal places (for numeric inputs) */\n precision?: number;\n /** Input type - numeric or text */\n inputType?: 'numeric' | 'text';\n /** Label for the input */\n label?: string;\n /** Whether the input is read-only */\n readOnly?: boolean;\n /**\n * True when `value` is a maintenance force, not a measured reading\n * (#3607, R19). Renders a FORCED badge; never changes `status`.\n */\n forced?: boolean;\n /**\n * The real, unforced value, shown beside `value` when `forced` is true.\n * Optional; absent until the backend publishes it (unit U3b).\n */\n realValue?: number | string;\n}\n\nexport const LegacyValueEntry: React.FC<LegacyValueEntryProps> = ({\n value,\n onChange,\n unit,\n status = 'normal',\n min,\n max,\n precision = 2,\n inputType = 'numeric',\n label,\n readOnly = false,\n forced = false,\n realValue,\n className = '',\n disabled,\n ...props\n}) => {\n const [displayValue, setDisplayValue] = useState(String(value));\n const [isEditing, setIsEditing] = useState(false);\n\n const handleFocus = useCallback(() => {\n setIsEditing(true);\n setDisplayValue(String(value));\n }, [value]);\n\n const handleBlur = useCallback(() => {\n setIsEditing(false);\n let newValue: number | string = displayValue;\n\n if (inputType === 'numeric') {\n const numValue = parseFloat(displayValue);\n if (!isNaN(numValue)) {\n // Apply min/max constraints\n let constrainedValue = numValue;\n if (min !== undefined) constrainedValue = Math.max(min, constrainedValue);\n if (max !== undefined) constrainedValue = Math.min(max, constrainedValue);\n\n // Apply precision\n constrainedValue = parseFloat(constrainedValue.toFixed(precision));\n newValue = constrainedValue;\n setDisplayValue(String(constrainedValue));\n } else {\n // Invalid number, revert to original value\n setDisplayValue(String(value));\n return;\n }\n }\n\n if (newValue !== value) {\n onChange(newValue);\n }\n }, [displayValue, inputType, min, max, precision, value, onChange]);\n\n const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n setDisplayValue(e.target.value);\n }, []);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.currentTarget.blur();\n }\n if (e.key === 'Escape') {\n setDisplayValue(String(value));\n e.currentTarget.blur();\n }\n },\n [value]\n );\n\n // Create the display text\n const displayText = isEditing\n ? displayValue\n : inputType === 'numeric'\n ? Number(value).toFixed(precision)\n : String(value);\n\n // Status-based styling with modern industrial colors\n const getStatusStyles = () => {\n switch (status) {\n case 'alarm':\n return {\n borderColor: '#ef4444',\n backgroundColor: 'rgba(239, 68, 68, 0.15)',\n color: '#fecaca',\n boxShadow: '0 0 20px rgba(239, 68, 68, 0.3)',\n };\n case 'warning':\n return {\n borderColor: '#f59e0b',\n backgroundColor: 'rgba(245, 158, 11, 0.15)',\n color: '#fed7aa',\n boxShadow: '0 0 20px rgba(245, 158, 11, 0.3)',\n };\n case 'disabled':\n return {\n borderColor: '#6b7280',\n backgroundColor: 'rgba(107, 114, 128, 0.1)',\n color: '#9ca3af',\n cursor: 'not-allowed',\n };\n default:\n return {\n borderColor: '#64748b',\n backgroundColor: 'rgba(30, 41, 59, 0.9)',\n color: '#f1f5f9',\n boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',\n };\n }\n };\n\n const statusStyles = getStatusStyles();\n\n const forcedBadgeStyle: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '1.1rem',\n height: '1.1rem',\n fontSize: '0.7rem',\n fontWeight: 700,\n color: FORCED_MARK_COLOR.text,\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n borderRadius: '50%',\n marginLeft: '8px',\n lineHeight: 1,\n };\n\n const realValueStyle: React.CSSProperties = {\n fontSize: '0.75rem',\n fontWeight: 500,\n color: '#94a3b8',\n letterSpacing: '0.02em',\n };\n\n const inputStyle: React.CSSProperties = {\n width: '12rem',\n height: '4rem',\n padding: unit ? '1rem 4rem 1rem 1.5rem' : '1rem 1.5rem',\n border: '2px solid',\n borderRadius: '1.5rem',\n fontFamily:\n 'ui-monospace, SFMono-Regular, \"SF Mono\", Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace',\n fontSize: '1.25rem',\n fontWeight: 'bold',\n textAlign: 'center',\n outline: 'none',\n transition: 'all 300ms ease',\n backdropFilter: 'blur(8px)',\n ...statusStyles,\n };\n\n const inputStyleFocus: React.CSSProperties = {\n ...inputStyle,\n borderColor: '#60a5fa',\n boxShadow:\n '0 0 0 4px rgba(96, 165, 250, 0.3), ' +\n (statusStyles.boxShadow || '0 8px 32px rgba(0, 0, 0, 0.3)'),\n transform: 'scale(1.02)',\n };\n\n const unitStyle: React.CSSProperties = {\n position: 'absolute',\n right: '0.75rem',\n top: '50%',\n transform: 'translateY(-50%)',\n padding: '0.5rem 0.75rem',\n fontSize: '1rem',\n fontWeight: 'bold',\n color: '#ffffff',\n backgroundColor: '#3b82f6',\n borderRadius: '0.5rem',\n border: '2px solid #60a5fa',\n backdropFilter: 'blur(4px)',\n pointerEvents: 'none' as const,\n zIndex: 10,\n boxShadow: '0 2px 8px rgba(59, 130, 246, 0.4)',\n };\n\n return (\n <div\n style={{\n display: 'inline-flex',\n flexDirection: 'column',\n alignItems: 'center',\n gap: '0.75rem',\n }}\n >\n {(label || forced) && (\n <label\n style={{\n display: 'flex',\n alignItems: 'center',\n fontSize: '0.875rem',\n fontWeight: '600',\n color: '#cbd5e1',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }}\n >\n {label}\n {forced && (\n <span style={forcedBadgeStyle} aria-label=\"Forced\">\n F\n </span>\n )}\n </label>\n )}\n <div style={{ position: 'relative', display: 'inline-flex', alignItems: 'center' }}>\n <input\n type=\"text\"\n value={displayText}\n onChange={handleChange}\n onFocus={(e) => {\n Object.assign(e.target.style, inputStyleFocus);\n handleFocus();\n }}\n onBlur={(e) => {\n Object.assign(e.target.style, inputStyle);\n handleBlur();\n }}\n onKeyDown={handleKeyDown}\n disabled={disabled}\n readOnly={readOnly}\n style={inputStyle}\n {...props}\n />\n {unit && <span style={unitStyle}>{unit}</span>}\n {status === 'alarm' && (\n <div\n style={{\n position: 'absolute',\n top: '-8px',\n right: '-8px',\n width: '1rem',\n height: '1rem',\n backgroundColor: '#ef4444',\n borderRadius: '50%',\n boxShadow: '0 0 10px rgba(239, 68, 68, 0.8)',\n animation: 'pulse 2s infinite',\n }}\n />\n )}\n {status === 'warning' && (\n <div\n style={{\n position: 'absolute',\n top: '-8px',\n right: '-8px',\n width: '1rem',\n height: '1rem',\n backgroundColor: '#f59e0b',\n borderRadius: '50%',\n boxShadow: '0 0 10px rgba(245, 158, 11, 0.8)',\n }}\n />\n )}\n </div>\n {forced && realValue !== undefined && (\n <div style={realValueStyle}>\n REAL {String(realValue)}\n {unit ? ` ${unit}` : ''}\n </div>\n )}\n </div>\n );\n};\n","/**\n * ISA-101 High Performance HMI — Unified Status Color System\n *\n * Single source of truth for mapping device status to visual colors.\n * Used by NodeRenderer (SVG fill/stroke), DeviceControlPanel (status indicator),\n * and DataOverlay (telemetry badge).\n */\n\n/**\n * Canonical device status vocabulary.\n *\n * `unknown` marks a value the backend does not trust. OPC UA publishes this\n * condition as StatusCode severity Bad. The library never derives `unknown`\n * from a number or a threshold. The library only displays an `unknown` value\n * that the backend has already published.\n */\nexport type DeviceStatus =\n | 'normal'\n | 'warning'\n | 'alarm'\n | 'fault'\n | 'off'\n | 'starting'\n | 'stopping'\n | 'unknown'\n | 'forced';\n\n/** Color entry covering both SVG node and overlay/panel needs */\nexport interface StatusColorEntry {\n /** Fill color for SVG node symbols */\n fill: string;\n /** Stroke color for SVG node symbols */\n stroke: string;\n /** Background color for overlays and panel indicators */\n bg: string;\n /** Text color for overlays */\n text: string;\n /** Border color for overlays */\n border: string;\n}\n\n/** Partial map for consumer overrides — only specify statuses you want to change */\nexport type StatusColorMap = Partial<Record<DeviceStatus, Partial<StatusColorEntry>>>;\n\n/**\n * The ISA unknown token, shared with the consuming application.\n *\n * The same token already marks an untrusted value on the consumer's Sidebar\n * and Interlocks screens. The library reuses it so one meaning has one\n * appearance across both products. See views-library issue #27.\n *\n * The library uses this one token for both unknown triggers: a node with\n * `status: 'unknown'` (an untrusted device) and a node with\n * `positionUnknown: true` (a disclaimed position). Both mean that the system\n * cannot confirm the state, so both get the same appearance.\n *\n * The hatch carries the meaning, not the fill. The flat `bg` sits a CIEDE2000\n * delta-E of 0.45 from the `off` fill after greyscale conversion, so the fill\n * alone cannot separate an untrusted device from a confirmed-off one. Any\n * surface that uses this token must also carry the hatch or a glyph.\n *\n * The hue is deliberate. Periwinkle at hue 240 sits between override violet\n * and interaction blue, so an operator cannot confuse the three.\n */\nexport const ISA_UNKNOWN = {\n fg: '#4f4f8a',\n bg: '#ebebf5',\n border: '#8f8fc2',\n} as const;\n\n/**\n * The dark-theme counterpart of `ISA_UNKNOWN`.\n *\n * `DEFAULT_STATUS_COLORS` carries the light values. Wiring a theme switch is\n * not part of this change; the constant exists so a consumer and a later\n * change can use the agreed values rather than inventing new ones.\n */\nexport const ISA_UNKNOWN_DARK = {\n fg: '#b9bbdb',\n bg: '#23263a',\n border: '#6f74a3',\n} as const;\n\n/**\n * Maintenance-force marking color (#3607, R19). A forced value is not a\n * health status, and must never share a color with warning/alarm/fault or\n * read as one of them. Violet is unused elsewhere in this palette.\n *\n * `DEFAULT_STATUS_COLORS.forced` points at this same object (alphinity-tffi\n * #3771). A device that a maintenance force holds then gets the forced\n * violet on its status dot, and not the `off` grey fallback. The forced\n * entry is a marker, not a severity: it stays outside the\n * warning/alarm/fault colours.\n *\n * Light fill + dark text, not a solid vivid chip: reads as a calm marker\n * rather than an alarm sticker, while the vivid `border` shade still\n * outlines a forced value's box/ring so it stays findable at a glance.\n */\nexport const FORCED_MARK_COLOR: StatusColorEntry = {\n fill: '#ede9fe',\n stroke: '#5b21b6',\n bg: '#ede9fe',\n text: '#5b21b6',\n border: '#7c3aed',\n};\n\n/**\n * ISA-101 defaults.\n *\n * - normal: muted grey (ISA-101: no color for normal operation)\n * - warning: amber/yellow\n * - alarm: red\n * - fault: dark red\n * - off: light grey (matches canvas default #edeeef)\n * - starting: light green (transitional)\n * - stopping: amber (transitional)\n * - unknown: periwinkle (untrusted read; OPC UA severity Bad). Derived\n * from ISA_UNKNOWN above, the shared token.\n * - forced: violet (a maintenance force holds the device). The same\n * object as FORCED_MARK_COLOR above.\n *\n * The unknown entry deliberately does NOT rely on its fill. The flat fill\n * sits a CIEDE2000 delta-E of 0.45 from the off fill after greyscale\n * conversion, so the two are indistinguishable without colour.\n *\n * Every unknown surface therefore carries a second channel that does not\n * depend on colour. Fixed-layout surfaces add a glyph. The P&ID symbol adds\n * a hatch. A surface that shows this fill alone reproduces the defect the\n * unknown status exists to close.\n *\n * This palette is not fully greyscale-separable: normal and alarm sit 1.6\n * apart. That is why unknown never relies on colour anywhere.\n */\nexport const DEFAULT_STATUS_COLORS: Record<DeviceStatus, StatusColorEntry> = {\n normal: { fill: '#888888', stroke: '#6b6b6b', bg: '#888888', text: '#ffffff', border: '#6b6b6b' },\n warning: { fill: '#f59e0b', stroke: '#d97706', bg: '#FFD700', text: '#000000', border: '#d97706' },\n alarm: { fill: '#ef4444', stroke: '#dc2626', bg: '#FF0000', text: '#ffffff', border: '#dc2626' },\n fault: { fill: '#dc2626', stroke: '#b91c1c', bg: '#FF0000', text: '#ffffff', border: '#b91c1c' },\n off: { fill: '#edeeef', stroke: '#000000', bg: '#666666', text: '#ffffff', border: '#4b5563' },\n starting: { fill: '#90EE90', stroke: '#22c55e', bg: '#90EE90', text: '#000000', border: '#22c55e' },\n stopping: { fill: '#FFD700', stroke: '#d97706', bg: '#FFD700', text: '#000000', border: '#d97706' },\n unknown: { fill: ISA_UNKNOWN.bg, stroke: ISA_UNKNOWN.border, bg: ISA_UNKNOWN.bg, text: ISA_UNKNOWN.fg, border: ISA_UNKNOWN.border },\n forced: FORCED_MARK_COLOR,\n};\n\n\n/** Backward-compatible aliases for old status vocabulary */\nconst STATUS_ALIASES: Record<string, DeviceStatus> = {\n running: 'normal',\n stopped: 'off',\n};\n\n/**\n * Normalize a status string to its canonical `DeviceStatus` key.\n *\n * The function lowercases the value and follows `STATUS_ALIASES`. Every\n * caller that must decide \"is this status X?\" uses this function. A caller\n * that compares a raw string instead can disagree with `resolveStatusColor`\n * once an alias changes, which would let one surface paint a status the\n * other surface does not recognise.\n */\nexport function normalizeDeviceStatus(status: string | undefined): DeviceStatus {\n const raw = (status || 'off').toLowerCase();\n return (STATUS_ALIASES[raw] ?? raw) as DeviceStatus;\n}\n\n/**\n * Resolve a status string to a full color entry.\n *\n * Priority: customMap[status] (merged) > DEFAULT_STATUS_COLORS[status] > off fallback.\n * Supports aliases ('running' -> 'normal', 'stopped' -> 'off').\n */\nexport function resolveStatusColor(\n status: string | undefined,\n customMap?: StatusColorMap,\n): StatusColorEntry {\n const key = normalizeDeviceStatus(status);\n const defaults = DEFAULT_STATUS_COLORS[key] ?? DEFAULT_STATUS_COLORS.off;\n const overrides = customMap?.[key];\n if (!overrides) return defaults;\n return { ...defaults, ...overrides };\n}\n","import React from 'react';\nimport {\n UnknownStatusGlyph,\n UNKNOWN_GLYPH_FILL_STYLE,\n unknownGlyphHostStyle,\n} from '../../theme/unknownStatusGlyph';\n\nconst getStatusDescription = (status: string): string => {\n switch (status) {\n case 'alarm':\n return 'Critical alarm condition requiring attention';\n case 'warning':\n return 'Warning condition that should be monitored';\n case 'fault':\n return 'Equipment fault detected';\n case 'active':\n return 'Equipment is actively running';\n case 'normal':\n return 'Operating normally';\n case 'off':\n return 'Equipment is off';\n case 'unknown':\n return 'Device state cannot be confirmed. Check the link or the wiring.';\n default:\n return '';\n }\n};\n\n// Hide the description node with an inline style, not a CSS class. The\n// package ships no stylesheet, so a class-based `sr-only` rule would never\n// reach a consumer. This is the standard visually-hidden clip technique: it\n// keeps the text readable by assistive tech without affecting layout.\nconst visuallyHiddenStyle: React.CSSProperties = {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: 0,\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: 0,\n};\n\nexport interface StatusIndicatorProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Status state determining color and behavior.\n *\n * `unknown` marks a value the backend does not trust. Set `unknown` only\n * from a published bad-quality read. Do not derive `unknown` from a number\n * or a threshold in this component.\n */\n status: 'off' | 'normal' | 'alarm' | 'warning' | 'active' | 'fault' | 'unknown';\n /** Size of the indicator */\n size?: 'small' | 'medium' | 'large';\n /**\n * Animation behavior. Default is `none`.\n *\n * ISA-18.2 reserves blinking for an unacknowledged alarm. The `unknown`\n * status does not clamp this prop to `none`. Pass `animation` only when\n * the alarm state of the point needs it.\n */\n animation?: 'none' | 'pulse' | 'blink' | 'flash';\n /** Label text to display next to indicator */\n label?: string;\n /** Position of label relative to indicator */\n labelPosition?: 'left' | 'right' | 'top' | 'bottom';\n /** Shape of the indicator */\n shape?: 'circle' | 'square' | 'diamond';\n /** Whether the indicator should have a 3D LED effect */\n led?: boolean;\n /** Whether the indicator is rendered at all. When false, nothing is rendered. Defaults to true. */\n visible?: boolean;\n /**\n * True when the underlying tag's state is a maintenance force, not a\n * measured reading (#3607, R19). Renders a small glyph badge, never a\n * color change: this indicator's whole signal is color plus a short\n * label today, and color alone fails a colorblind operator.\n */\n forced?: boolean;\n /**\n * The real, unforced state, shown as text beside `label` when `forced`\n * is true. For a forced discrete OUTPUT (a lamp, a valve OPEN/CLOSED\n * indicator - R2), this is the state the system would command if the\n * force were not active, not a second physical reading. Optional:\n * absent until the backend publishes it. When absent, only the forced\n * glyph badge renders.\n */\n realValue?: string;\n}\n\nexport const StatusIndicator: React.FC<StatusIndicatorProps> = ({\n status,\n size = 'medium',\n animation = 'none',\n label,\n labelPosition = 'right',\n shape = 'circle',\n led = true,\n visible = true,\n forced = false,\n realValue,\n className = '',\n style,\n 'aria-describedby': consumerDescribedBy,\n children,\n ...props\n}) => {\n // A stable id per instance, so two indicators on one page never collide.\n // Call the hook before the `visible` early return below. A hook after a\n // conditional return breaks the Rules of Hooks when `visible` toggles.\n const generatedDescriptionId = `${React.useId()}-status-description`;\n\n if (!visible) {\n return null;\n }\n\n // Compose, do not choose: `aria-describedby` accepts a space-separated\n // list of ids. Append the library's id to a consumer-supplied one so\n // neither description is lost.\n const describedBy = consumerDescribedBy\n ? `${consumerDescribedBy} ${generatedDescriptionId}`\n : generatedDescriptionId;\n\n const description = getStatusDescription(status);\n\n // Use CSS classes for base, status, size, shape, animation, led\n const baseClass = 'ui-status-indicator';\n const statusClass = status ? `ui-status-indicator--${status}` : '';\n const sizeClass = size ? `ui-status-indicator--${size}` : '';\n const shapeClass = shape ? `ui-status-indicator--${shape}` : '';\n const animationClass = animation ? `ui-status-indicator--${animation}` : '';\n const ledClass = led ? 'ui-status-indicator--led' : '';\n\n const isUnknown = status === 'unknown';\n\n // The host element has no `position` set by the library elsewhere. The\n // shared helper sets `relative` only for the `unknown` glyph, so the glyph\n // can fill the element with an absolute inset. No consumer CSS is needed.\n const indicatorStyle = unknownGlyphHostStyle(isUnknown, style);\n\n // Container layout based on label position\n const getContainerLayout = () => {\n switch (labelPosition) {\n case 'left':\n return 'ui-status-indicator-container ui-status-indicator-label-left';\n case 'right':\n return 'ui-status-indicator-container ui-status-indicator-label-right';\n case 'top':\n return 'ui-status-indicator-container ui-status-indicator-label-top';\n case 'bottom':\n return 'ui-status-indicator-container ui-status-indicator-label-bottom';\n default:\n return 'ui-status-indicator-container ui-status-indicator-label-right';\n }\n };\n\n const forcedClass = forced ? 'ui-status-indicator--forced' : '';\n\n const hasRealValue = forced && realValue !== undefined;\n\n return (\n <span className={getContainerLayout()}>\n {label && (labelPosition === 'left' || labelPosition === 'top') && (\n <span className=\"ui-status-indicator-label\">{label}</span>\n )}\n {hasRealValue && (labelPosition === 'left' || labelPosition === 'top') && (\n <span className=\"ui-status-indicator-real-value\">REAL {realValue}</span>\n )}\n <span style={{ position: 'relative', display: 'inline-flex' }}>\n <span\n role=\"status\"\n aria-label={label || `Status: ${status}`}\n aria-description={description}\n aria-describedby={describedBy}\n data-status={status}\n data-forced={forced}\n className={`\n ${baseClass}\n ${statusClass}\n ${sizeClass}\n ${shapeClass}\n ${animationClass}\n ${ledClass}\n ${forcedClass}\n ${className}\n `\n .replace(/\\s+/g, ' ')\n .trim()}\n style={indicatorStyle}\n {...props}\n >\n {isUnknown && (\n <UnknownStatusGlyph\n size={size}\n counterRotate={shape === 'diamond'}\n style={UNKNOWN_GLYPH_FILL_STYLE}\n />\n )}\n {/* Keep rendering any children the consumer passed. The element used\n to be self-closing, so children arrived through the props spread;\n explicit children would otherwise drop them silently. */}\n {children}\n <span id={generatedDescriptionId} style={visuallyHiddenStyle}>\n {description}\n </span>\n </span>\n {/* The forced badge (#26) overhangs the top-right corner, outside the\n indicator body that the unknown glyph fills. A value can be both\n forced and untrusted, so the two markers must stay separately\n readable. */}\n {forced && (\n <span className=\"ui-status-indicator-forced-badge\" aria-label=\"Forced\">\n F\n </span>\n )}\n </span>\n {label && (labelPosition === 'right' || labelPosition === 'bottom') && (\n <span className=\"ui-status-indicator-label\">{label}</span>\n )}\n {hasRealValue && (labelPosition === 'right' || labelPosition === 'bottom') && (\n <span className=\"ui-status-indicator-real-value\">REAL {realValue}</span>\n )}\n </span>\n );\n};\n","import React from 'react';\n\n/**\n * Shared glyph for the `unknown` device status (KTD10).\n *\n * `StatusIndicator`, `DataOverlay`, and `DeviceControlPanel` import this one\n * module. One drawing keeps the marker consistent. An operator learns the\n * marker once and recognises it on every surface.\n *\n * The glyph is a question mark inside a dashed ring. The dashed ring is a\n * second channel that does not depend on colour. A broken outline reads as\n * an incomplete circuit.\n */\n\n/** Size keys the glyph accepts. These match `StatusIndicator`'s `size` prop. */\nexport type UnknownGlyphSize = 'small' | 'medium' | 'large';\n\n/**\n * CSS custom property that colours the glyph.\n *\n * A consumer sets this property on any ancestor to retheme the glyph.\n * `StatusIndicator` scopes it under the `ui-status-indicator--unknown` class.\n * The overlay and the control panel carry no such class, so a consumer sets\n * the property directly on those hosts. The glyph falls back to\n * `currentColor` when the consumer sets nothing, so the package never has to\n * ship CSS for it.\n */\nexport const UNKNOWN_STATUS_GLYPH_COLOR_VAR = '--ui-status-indicator-unknown-glyph-color';\n\ninterface UnknownGlyphStrokeWeights {\n /** Stroke width of the dashed ring. */\n ring: number;\n /** Dash pattern of the ring, as an SVG `stroke-dasharray` value. */\n ringDash: string;\n /** Stroke width of the question-mark hook. */\n mark: number;\n /** Radius of the question-mark dot. */\n dot: number;\n}\n\n/**\n * Per-size stroke weights.\n *\n * Small sizes use heavier strokes. A heavier stroke keeps the 3:1 contrast\n * floor when the whole indicator is only 1rem wide.\n */\nconst UNKNOWN_GLYPH_STROKE_WEIGHTS: Record<UnknownGlyphSize, UnknownGlyphStrokeWeights> = {\n small: { ring: 4.5, ringDash: '7 6', mark: 5, dot: 2.9 },\n medium: { ring: 4, ringDash: '6.5 5.5', mark: 4.5, dot: 2.7 },\n large: { ring: 3.5, ringDash: '6.5 5.5', mark: 4, dot: 2.5 },\n};\n\n/**\n * Dash rhythm of the glyph's ring.\n *\n * The P&ID node symbol reuses this value for its own dashed outline. The\n * canvas cannot host a centred glyph, so it carries the same rhythm on the\n * symbol outline instead. Sharing the value keeps the two treatments in step.\n */\nexport const UNKNOWN_GLYPH_RING_DASH = UNKNOWN_GLYPH_STROKE_WEIGHTS.medium.ringDash;\n\n/**\n * Style that makes the glyph fill its host box.\n *\n * Use it with `unknownGlyphHostStyle` on an HTML host. An SVG host places the\n * glyph with `x`, `y`, `width` and `height` instead and must not use this\n * style. CSS `width` and `height` override the `width` and `height`\n * presentation attributes on an `svg` element, so a forced size here would\n * silently defeat an SVG host's own sizing.\n */\nexport const UNKNOWN_GLYPH_FILL_STYLE: React.CSSProperties = {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n};\n\n/**\n * Position an HTML host so the glyph can fill it.\n *\n * The host needs `position: relative` only while it shows the glyph. Any\n * caller-supplied style wins, so a consumer can still override the position.\n */\nexport function unknownGlyphHostStyle(\n isUnknown: boolean,\n style?: React.CSSProperties,\n): React.CSSProperties | undefined {\n return isUnknown ? { position: 'relative', ...style } : style;\n}\n\nexport interface UnknownStatusGlyphProps\n extends Omit<React.SVGProps<SVGSVGElement>, 'children' | 'viewBox'> {\n /** Size of the glyph. Sets the stroke weight (see `UNKNOWN_GLYPH_STROKE_WEIGHTS`). */\n size?: UnknownGlyphSize;\n /**\n * Counter-rotate the glyph.\n *\n * Set this to `true` when the host element applies `transform: rotate(45deg)`,\n * such as the `diamond` shape in `StatusIndicator`. The glyph then reads\n * upright instead of inheriting the host rotation.\n *\n * The counter-rotation is a fixed -45 degrees. It assumes the 45-degree\n * rotation that the documented `ui-status-indicator--diamond` rule applies.\n * A consumer that rotates the host by another angle must not set this flag.\n */\n counterRotate?: boolean;\n}\n\n/**\n * Renders the `unknown` status glyph as an inline `svg`.\n *\n * The glyph carries no CSS class dependency. It needs no stylesheet, so it\n * survives server-side rendering and needs no CSP nonce (KTD1).\n *\n * The `svg` is `aria-hidden` and `focusable=\"false\"`. A separate unit adds\n * the accessible description.\n *\n * Remaining `svg` props pass straight through. An HTML host sizes the glyph\n * with `style`; an SVG host places it with `x`, `y`, `width`, and `height`,\n * because a nested `svg` accepts those as user-unit attributes.\n */\nexport function UnknownStatusGlyph({\n size = 'medium',\n counterRotate = false,\n style,\n ...rest\n}: UnknownStatusGlyphProps): React.ReactElement {\n const weights = UNKNOWN_GLYPH_STROKE_WEIGHTS[size];\n\n return (\n <svg\n viewBox=\"0 0 48 48\"\n aria-hidden=\"true\"\n focusable=\"false\"\n style={{\n display: 'block',\n color: `var(${UNKNOWN_STATUS_GLYPH_COLOR_VAR}, currentColor)`,\n transform: counterRotate ? 'rotate(-45deg)' : undefined,\n ...style,\n }}\n {...rest}\n >\n <circle\n cx={24}\n cy={24}\n r={20}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeDasharray={weights.ringDash}\n strokeLinecap=\"round\"\n strokeWidth={weights.ring}\n />\n <path\n d=\"M18.4 18.6a5.8 5.8 0 1 1 6.4 8.1v3.1\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={weights.mark}\n />\n <circle cx={24.6} cy={35.4} r={weights.dot} fill=\"currentColor\" />\n </svg>\n );\n}\n","import React from 'react';\nimport { ControlPanel, type ModeConfig } from '../ControlPanel';\nimport type { SelectorOption } from '../Selector';\n\nexport interface NodeControlConfig {\n /** Node ID */\n nodeId: string;\n /** Display title for the control panel */\n title: string;\n /** Available mode options */\n modes: SelectorOption[];\n /** Current selected mode */\n currentMode: string;\n /** Callback when mode changes */\n onModeChange: (mode: string) => void;\n /** Typed/target setpoint value (editable) */\n setpoint: number | string;\n /** Callback when setpoint changes */\n onSetpointChange: (value: number | string) => void;\n /** Current setpoint: read-only feedback from live telemetry. See ControlPanelProps.currentSetpointValue. */\n currentSetpoint?: number | string;\n /** True when `currentSetpoint` is a maintenance force, not a measured reading. */\n currentSetpointForced?: boolean;\n /** The real, unforced value for `currentSetpoint`. Optional; absent until the backend publishes it. */\n currentSetpointRealValue?: number | string;\n /** Optional second setpoint value */\n secondSetpoint?: number | string;\n /** Callback for second setpoint */\n onSecondSetpointChange?: (value: number | string) => void;\n /** Configuration for each mode */\n modeConfigs?: Record<string, ModeConfig>;\n /** Whether device is currently running */\n isRunning?: boolean;\n /** Start command handler */\n onStart?: () => void;\n /** Stop command handler */\n onStop?: () => void;\n /** Custom start button text */\n startButtonText?: string;\n /** Custom stop button text */\n stopButtonText?: string;\n /** Current status */\n status?: 'normal' | 'alarm' | 'warning' | 'disabled';\n /** Whether setpoint is read-only */\n readOnly?: boolean;\n}\n\nexport interface NodeControlsPanelProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Control configuration for the selected node */\n config: NodeControlConfig | null;\n /** Callback to close the panel */\n onClose: () => void;\n /** Panel position - bottom (mobile) or side (desktop/large touch screens) */\n position?: 'bottom' | 'side';\n /** Whether to show in touch-optimized mode (larger targets) */\n touchOptimized?: boolean;\n}\n\n/**\n * NodeControlsPanel - Touch-friendly slide-out panel for device manual controls\n *\n * Optimized for industrial touch screens with:\n * - Large touch targets (minimum 44px)\n * - Single-tap interactions\n * - Clear visual feedback\n * - Bottom or side slide-in animation\n */\nexport const NodeControlsPanel: React.FC<NodeControlsPanelProps> = ({\n config,\n onClose,\n position = 'bottom',\n touchOptimized = true,\n className = '',\n style,\n ...props\n}) => {\n if (!config) return null;\n\n const isBottom = position === 'bottom';\n const touchClass = touchOptimized ? 'touch-optimized' : '';\n\n // Container styles\n const containerStyle: React.CSSProperties = {\n position: 'fixed',\n zIndex: 9999,\n background: 'white',\n boxShadow: '0 -4px 16px rgba(0, 0, 0, 0.15)',\n borderRadius: isBottom ? '16px 16px 0 0' : '0',\n animation: isBottom ? 'slideUp 0.3s ease-out' : 'slideInRight 0.3s ease-out',\n display: 'flex',\n flexDirection: 'column',\n ...(isBottom\n ? {\n left: 0,\n right: 0,\n bottom: 0,\n maxHeight: '70vh',\n borderTop: '1px solid #e5e7eb',\n }\n : {\n right: 0,\n top: 0,\n bottom: 0,\n width: touchOptimized ? '400px' : '320px',\n borderLeft: '1px solid #e5e7eb',\n }),\n ...style,\n };\n\n // Header styles\n const headerStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: touchOptimized ? '1.25rem 1.5rem' : '1rem',\n borderBottom: '2px solid #e5e7eb',\n background: '#f9fafb',\n flexShrink: 0,\n };\n\n const titleStyle: React.CSSProperties = {\n margin: 0,\n fontSize: touchOptimized ? '1.5rem' : '1.25rem',\n fontWeight: 600,\n color: '#111827',\n };\n\n // Close button styles (large touch target)\n const closeButtonStyle: React.CSSProperties = {\n background: '#ef4444',\n color: 'white',\n border: 'none',\n borderRadius: '8px',\n width: touchOptimized ? '56px' : '44px',\n height: touchOptimized ? '56px' : '44px',\n fontSize: touchOptimized ? '1.75rem' : '1.5rem',\n fontWeight: 'bold',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'all 0.2s ease',\n flexShrink: 0,\n };\n\n const contentStyle: React.CSSProperties = {\n padding: touchOptimized ? '1.5rem' : '1rem',\n overflowY: 'auto',\n flex: 1,\n };\n\n return (\n <>\n {/* Backdrop */}\n <div\n style={{\n position: 'fixed',\n inset: 0,\n background: 'rgba(0, 0, 0, 0.3)',\n zIndex: 9998,\n animation: 'fadeIn 0.3s ease-out',\n }}\n onClick={onClose}\n />\n\n {/* Panel */}\n <div\n className={`node-controls-panel ${touchClass} ${className}`.trim()}\n style={containerStyle}\n {...props}\n >\n {/* Header */}\n <div style={headerStyle}>\n <h2 style={titleStyle}>{config.title}</h2>\n <button\n style={closeButtonStyle}\n onClick={onClose}\n onMouseEnter={(e) => {\n e.currentTarget.style.background = '#dc2626';\n e.currentTarget.style.transform = 'scale(1.05)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = '#ef4444';\n e.currentTarget.style.transform = 'scale(1)';\n }}\n aria-label=\"Close\"\n >\n ✕\n </button>\n </div>\n\n {/* Content */}\n <div style={contentStyle}>\n <ControlPanel\n title={config.nodeId}\n selectedMode={config.currentMode}\n modeOptions={config.modes}\n onModeChange={config.onModeChange}\n setpointValue={config.setpoint}\n onSetpointChange={config.onSetpointChange}\n currentSetpointValue={config.currentSetpoint}\n currentSetpointForced={config.currentSetpointForced}\n currentSetpointRealValue={config.currentSetpointRealValue}\n secondSetpointValue={config.secondSetpoint}\n onSecondSetpointChange={config.onSecondSetpointChange}\n modeConfigs={config.modeConfigs}\n status={config.status}\n setpointReadOnly={config.readOnly}\n showStartStopButtons={!!(config.onStart || config.onStop)}\n isRunning={config.isRunning}\n onStart={config.onStart}\n onStop={config.onStop}\n startButtonText={config.startButtonText}\n stopButtonText={config.stopButtonText}\n size={touchOptimized ? 'large' : 'medium'}\n />\n </div>\n </div>\n\n {/* Animations */}\n <style>{`\n @keyframes slideUp {\n from {\n transform: translateY(100%);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n }\n\n @keyframes slideInRight {\n from {\n transform: translateX(100%);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n }\n\n @keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n }\n\n .node-controls-panel.touch-optimized button {\n min-height: 56px;\n font-size: 1.125rem;\n }\n\n .node-controls-panel.touch-optimized input {\n min-height: 56px;\n font-size: 1.125rem;\n }\n `}</style>\n </>\n );\n};\n","/* eslint-disable react-hooks/set-state-in-effect */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport React, { useState, useEffect } from 'react';\nimport type { DeviceControlBinding } from '../../diagram/schema/DeviceControlBinding';\nimport { resolveStatusColor, normalizeDeviceStatus, FORCED_MARK_COLOR } from '../../theme/statusColors';\nimport {\n UnknownStatusGlyph,\n UNKNOWN_GLYPH_FILL_STYLE,\n unknownGlyphHostStyle,\n} from '../../theme/unknownStatusGlyph';\n\n/**\n * Disabled START / STOP look (alphinity-tffi #3853). The hatch and the dashed\n * border carry \"disabled\" without colour, as ISA-101 requires.\n */\nconst DISABLED_BUTTON_BG = '#e5e7eb';\nconst DISABLED_BUTTON_TEXT = '#4b5563';\nconst DISABLED_BUTTON_BORDER = '#6b7280';\nconst DISABLED_BUTTON_HATCH =\n 'repeating-linear-gradient(135deg, rgba(0,0,0,0.10) 0px, rgba(0,0,0,0.10) 4px, transparent 4px, transparent 10px)';\n\nexport interface DeviceControlPanelProps {\n /** Control binding to display */\n binding: DeviceControlBinding | null;\n /** Called when panel should close */\n onClose: () => void;\n /** Panel position */\n position?: 'bottom' | 'side';\n /** Horizontal alignment for bottom position (default: center) */\n align?: 'left' | 'center' | 'right';\n /** Optimize for touch (larger targets) */\n touchOptimized?: boolean;\n /** Compact mode - reduces padding and spacing */\n compact?: boolean;\n /** Font size scale (default: 1.0, smaller: 0.8, larger: 1.2) */\n fontScale?: number;\n /** Maximum height for bottom position (default: 70vh) */\n maxHeight?: string;\n /** Maximum width for bottom position (default: none) */\n maxWidth?: string;\n /** Toast notification duration in milliseconds (default: 3000) */\n toastDuration?: number;\n /** Called when mode changes */\n onModeChange?: (mode: string) => void;\n /** Called when parameter changes */\n onParameterChange?: (parameterId: string, value: any) => void;\n /**\n * Called when the operator activates (taps) a numeric/text parameter's value.\n * When provided, that parameter renders as a tappable value instead of an\n * inline input, so the consumer can open its own editor (e.g. a numpad dialog)\n * and commit via `onParameterChange`. Read-only params are never activatable.\n * When omitted, parameters use the default inline-edit behavior.\n */\n onParameterActivate?: (parameterId: string, value: number | string | boolean) => void;\n /** Called when start button clicked */\n onStart?: () => void;\n /** Called when stop button clicked */\n onStop?: () => void;\n /**\n * Start command is in flight (awaiting ack). START shows \"STARTING...\" and\n * is disabled, so a second tap cannot send a second start. STOP stays\n * enabled: a pending start never disables STOP (alphinity-tffi #3853).\n * Wire from the hook's `isCommandPending(nodeId, 'start')`, or from the\n * host's own in-flight state.\n */\n startPending?: boolean;\n /**\n * Stop command is in flight (awaiting ack). STOP shows \"STOPPING...\" and\n * stays enabled, so STOP is always one tap. Only `capabilities.canStop`\n * or `capabilities.stopEnabled` can disable STOP.\n */\n stopPending?: boolean;\n /** Called when custom action clicked */\n onCustomAction?: (actionId: string) => void;\n /** Called when config button clicked (opens config panel for this device) */\n onOpenConfig?: () => void;\n /** Show configuration button in header */\n showConfigButton?: boolean;\n /** Called when undock button clicked (optional, shows undock button if provided) */\n onUndock?: () => void;\n /** Icon to display in undock button (default: '⇱') */\n undockIcon?: string;\n /** Style for mode selector: 'buttons' (horizontal scroll), 'dropdown' (select), or 'modal' (click to open modal) */\n modeSelectionStyle?: 'buttons' | 'dropdown' | 'modal';\n /**\n * Render this panel's OWN result toast. Default true, so existing\n * consumers are unchanged.\n *\n * Set false when the host application already surfaces command and save\n * results through its own notification channel. Without it the operator\n * sees two notices for one action - this panel's optimistic \"command\n * sent\", in this panel's styling, followed by the host's message for the\n * real outcome - which reads as two different mechanisms.\n *\n * The result still lands in `binding.state`, so a host that opts out can\n * read it and render its own.\n */\n showBuiltInToast?: boolean;\n /** Embedded mode - uses static positioning for rendering in containers (default: false) */\n embedded?: boolean;\n}\n\n/**\n * Memoized below (kiosk heap-leak fix, same rationale as DeviceConfigPanel):\n * an OPEN control panel must not re-render on host ticks whose props did not\n * change. Unlike the config panel, a control binding legitimately changes when\n * its live values move, so the memo bails only on genuinely idle ticks - the\n * win is proportional to how often values are flat at display precision.\n */\nfunction DeviceControlPanelImpl({\n binding,\n onClose,\n position = 'bottom',\n align = 'center',\n touchOptimized = true,\n compact = false,\n fontScale = 1.0,\n maxHeight,\n maxWidth,\n toastDuration = 3000,\n onModeChange,\n onParameterChange,\n onParameterActivate,\n onStart,\n onStop,\n startPending = false,\n stopPending = false,\n onCustomAction,\n onOpenConfig,\n showConfigButton = true,\n onUndock,\n undockIcon = '⇱',\n modeSelectionStyle = 'buttons',\n showBuiltInToast = true,\n embedded = false,\n}: DeviceControlPanelProps) {\n const [localParameterValues, setLocalParameterValues] = useState<Record<string, any>>({});\n const [showModeModal, setShowModeModal] = useState(false);\n const [pendingMode, setPendingMode] = useState<string | null>(null);\n const [showToast, setShowToast] = useState(true);\n\n // Sync local parameter values with binding\n useEffect(() => {\n if (binding) {\n const currentParams = binding.parameters[binding.state.currentMode] || [];\n const values: Record<string, any> = {};\n currentParams.forEach((param) => {\n values[param.id] = param.value;\n });\n setLocalParameterValues(values);\n }\n }, [binding, binding?.state.currentMode]);\n\n // Auto-dismiss toast after configured duration\n useEffect(() => {\n if (binding?.state.lastCommandResult) {\n setShowToast(true);\n const timer = setTimeout(() => {\n setShowToast(false);\n }, toastDuration);\n return () => clearTimeout(timer);\n }\n }, [binding?.state.lastCommandResult, toastDuration]);\n\n if (!binding) return null;\n\n const currentMode = binding.state.currentMode;\n const currentParams = binding.parameters[currentMode] || [];\n const touchSize = touchOptimized ? 56 : 40;\n\n // Spacing and sizing based on compact mode\n const spacing = {\n padding: compact ? '12px' : '20px',\n gap: compact ? '6px' : '8px',\n marginBottom: compact ? '10px' : '16px',\n itemPadding: compact ? '8px 10px' : '10px 12px',\n };\n\n // Font sizes with scale applied\n const fontSize = {\n title: Math.round(14 * fontScale),\n label: Math.round(11 * fontScale),\n value: Math.round(13 * fontScale),\n input: Math.round(14 * fontScale),\n button: Math.round(12 * fontScale),\n status: Math.round(10 * fontScale),\n description: Math.round(13 * fontScale),\n };\n\n // Get current mode object to check for mode-specific capabilities\n const currentModeObj = binding.modes.find((m) => m.value === currentMode);\n\n // Use mode-specific capabilities if defined, otherwise fall back to binding-level capabilities\n const activeCapabilities = currentModeObj?.capabilities ?? binding.capabilities ?? {};\n\n // ISA-101 High Performance HMI colors - matched to telemetry theme\n // Standard: Gray backgrounds, minimal color except for alarms\n const backgroundColor = binding.theme?.backgroundColor || '#edeeef'; // Match telemetry background\n const surfaceColor = '#f5f6f7'; // Slightly lighter than background\n const borderColor = '#d2d2d2'; // Match canvas background\n const textColor = binding.theme?.textColor || '#000000'; // Black text\n const mutedTextColor = '#666666'; // Muted text\n const selectedColor = '#b4d0fe'; // Light blue accent (match trendlines)\n\n // ISA-101: Resolve status color from shared defaults, with per-binding theme overrides\n const statusEntry = resolveStatusColor(binding.state.status, binding.theme?.statusColors as any);\n const statusColor = statusEntry.bg;\n\n // An untrusted read carries a marker as well as a colour, because colour\n // alone cannot separate every status.\n const isUnknownStatus = normalizeDeviceStatus(binding.state.status) === 'unknown';\n\n // Fail open on telemetry: this panel never reads telemetry running-state\n // (which can lag) to disable START or STOP. A wrongly-disabled button locks\n // the operator out, and that costs more than a wrongly-enabled one.\n //\n // Fixed places (alphinity-tffi #3853): when the device has START or STOP,\n // the row shows BOTH buttons, each in its own place. A button that does not\n // apply is disabled. It is never removed, and the other button never takes\n // its place. Before #3853, START turned into STOP in the same place, so a\n // second tap (a double tap with gloves, or a tap again after a slow\n // response) stopped the pump that the first tap started.\n //\n // A button is disabled only when:\n // - the consumer says the command does not apply (`canStart` /\n // `canStop` false, or `startEnabled` / `stopEnabled` false), or\n // - START only: a start command is in flight (`startPending`).\n // STOP is never disabled by a pending command, so STOP is always one tap.\n // A consumer that passes none of these keeps both buttons enabled.\n const startStopRowShown =\n binding.display?.showStartStop !== false &&\n (activeCapabilities?.canStart === true || activeCapabilities?.canStop === true);\n // Custom actions in ONE fixed row (no wrap, no 120px minimum) when the\n // consumer asks for it; any other value keeps the default wrap layout.\n const customActionsInRow = binding.display?.customActionsLayout === 'row';\n const startEnabledNow =\n activeCapabilities?.canStart === true &&\n activeCapabilities?.startEnabled !== false &&\n !startPending;\n const stopEnabledNow =\n activeCapabilities?.canStop === true && activeCapabilities?.stopEnabled !== false;\n // Touch-first type: the START / STOP label is never smaller than 18px.\n const startStopFontPx = Math.max(18, Math.round(18 * fontScale));\n const startStopButtonStyle = (enabled: boolean, enabledBg: string): React.CSSProperties => ({\n flex: 1,\n minWidth: 0,\n height: Math.max(touchSize, 56),\n borderRadius: '6px',\n fontSize: `${startStopFontPx}px`,\n fontWeight: 700,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n ...(enabled\n ? {\n backgroundColor: enabledBg,\n color: '#ffffff',\n border: '2px solid transparent',\n cursor: 'pointer',\n opacity: 1,\n }\n : {\n // Disabled look that does not depend on colour (ISA-101): a dashed\n // border and a diagonal hatch. A grey fill alone is not enough.\n backgroundColor: DISABLED_BUTTON_BG,\n backgroundImage: DISABLED_BUTTON_HATCH,\n color: DISABLED_BUTTON_TEXT,\n border: `2px dashed ${DISABLED_BUTTON_BORDER}`,\n cursor: 'not-allowed',\n opacity: 1,\n }),\n });\n\n const handleModeChange = (mode: string) => {\n onModeChange?.(mode);\n };\n\n const handleParameterCommit = (parameterId: string, value: any) => {\n onParameterChange?.(parameterId, value);\n };\n\n const handleParameterInput = (parameterId: string, value: any) => {\n setLocalParameterValues((prev) => ({ ...prev, [parameterId]: value }));\n };\n\n // Panel styles\n const panelStyle: React.CSSProperties = embedded\n ? {\n // Embedded mode: static positioning for container layouts\n position: 'static',\n width: '100%',\n backgroundColor,\n color: textColor,\n borderRadius: '8px 8px 0 0',\n boxShadow: 'none',\n border: `2px solid ${borderColor}`,\n padding: spacing.padding,\n maxHeight: maxHeight || '70vh',\n overflowY: 'auto',\n ...binding.display?.style,\n }\n : {\n // Fixed mode: traditional overlay positioning\n position: 'fixed',\n ...(position === 'bottom'\n ? {\n bottom: 0,\n ...(align === 'left'\n ? { left: 0, right: 'auto', width: maxWidth }\n : align === 'right'\n ? { right: 0, left: 'auto', width: maxWidth }\n : {\n left: 0,\n right: 0,\n marginLeft: 'auto',\n marginRight: 'auto',\n maxWidth: maxWidth,\n }),\n animation: 'slideUp 0.3s ease-out',\n }\n : {\n right: 0,\n top: 0,\n bottom: 0,\n width: maxWidth || '400px',\n animation: 'slideIn 0.3s ease-out',\n }),\n backgroundColor,\n color: textColor,\n borderRadius: position === 'bottom' ? '8px 8px 0 0' : '8px 0 0 8px',\n boxShadow: 'none',\n border: `2px solid ${borderColor}`,\n padding: spacing.padding,\n zIndex: 9999,\n maxHeight: maxHeight || (position === 'bottom' ? '70vh' : '100vh'),\n overflowY: 'auto',\n ...binding.display?.style,\n };\n\n const titleText = binding.display?.title || binding.deviceName || binding.tag || binding.nodeId;\n\n return (\n <>\n {/* Panel */}\n <div style={panelStyle} className={binding.display?.className}>\n {/* Header */}\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: '16px',\n paddingBottom: '12px',\n borderBottom: `2px solid ${borderColor}`,\n }}\n >\n <div>\n <h3\n style={{\n margin: 0,\n fontSize: `${fontSize.title}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n color: textColor,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n maxWidth: '200px',\n }}\n title={titleText.length > 25 ? titleText : undefined}\n >\n {titleText.length > 25 ? titleText.slice(0, 25) + '...' : titleText}\n </h3>\n {binding.deviceType && (\n <div\n style={{\n fontSize: '11px',\n color: mutedTextColor,\n marginTop: '4px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {binding.deviceType}\n </div>\n )}\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n {/* Config button */}\n {showConfigButton && onOpenConfig && (\n <button\n onClick={onOpenConfig}\n title=\"Open Configuration\"\n style={{\n width: touchSize * 0.7,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: '16px',\n cursor: 'pointer',\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n padding: '0',\n }}\n >\n ⚙\n </button>\n )}\n {/* Undock button */}\n {onUndock && (\n <button\n onClick={onUndock}\n title=\"Undock panel\"\n style={{\n width: touchSize * 0.7,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: '16px',\n cursor: 'pointer',\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n padding: '0',\n }}\n >\n {undockIcon}\n </button>\n )}\n {/* Close button */}\n <button\n onClick={onClose}\n style={{\n width: touchSize * 0.7,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: '18px',\n cursor: 'pointer',\n fontWeight: 'bold',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n ×\n </button>\n </div>\n </div>\n\n {/* Status Indicator */}\n {binding.state.status && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n padding: spacing.itemPadding,\n backgroundColor: surfaceColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n marginBottom: spacing.marginBottom,\n }}\n >\n <div\n data-testid=\"device-status-dot\"\n style={{\n width: '10px',\n height: '10px',\n borderRadius: '50%',\n backgroundColor: statusColor,\n border: '1px solid #333',\n // The shared helper positions this dot only while it shows\n // the unknown marker. The glyph then fills the dot with an\n // absolute inset, the same pattern StatusIndicator uses.\n ...unknownGlyphHostStyle(isUnknownStatus),\n }}\n >\n {isUnknownStatus && (\n <UnknownStatusGlyph size=\"small\" style={UNKNOWN_GLYPH_FILL_STYLE} />\n )}\n </div>\n <div style={{ flex: 1 }}>\n <div\n style={{\n fontWeight: 600,\n textTransform: 'uppercase',\n fontSize: `${fontSize.label}px`,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n color: textColor,\n }}\n >\n {binding.state.status}\n </div>\n {binding.state.statusMessage && (\n <div\n style={{\n fontSize: '10px',\n color: mutedTextColor,\n marginTop: '2px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {binding.state.statusMessage}\n </div>\n )}\n </div>\n {binding.state.isRunning !== undefined && (\n <div\n style={{\n padding: '4px 10px',\n // This badge shows the last published isRunning value. It\n // never re-derives that value here. When status is\n // unknown, the value may be stale. Muted colors carry that\n // warning alongside the word, so color is not the only\n // channel and the wording is not the only channel either.\n backgroundColor: isUnknownStatus\n ? statusEntry.bg\n : binding.state.isRunning\n ? selectedColor\n : surfaceColor,\n border: `1px solid ${isUnknownStatus ? statusEntry.border : borderColor}`,\n borderRadius: '2px',\n fontSize: '10px',\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n color: isUnknownStatus ? statusEntry.text : textColor,\n }}\n >\n {binding.state.isRunning ? 'RUNNING' : 'STOPPED'}\n {isUnknownStatus && ' (unconfirmed)'}\n </div>\n )}\n </div>\n )}\n\n {/* Mode Selector */}\n {binding.display?.showModeSelector !== false && binding.modes.length > 0 && (\n <div style={{ marginBottom: '16px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '8px',\n fontSize: '11px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n fontFamily: 'Arial, sans-serif',\n color: mutedTextColor,\n }}\n >\n Mode\n </label>\n {modeSelectionStyle === 'dropdown' ? (\n <select\n value={currentMode}\n onChange={(e) => handleModeChange(e.target.value)}\n style={{\n width: '100%',\n height: touchSize,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n paddingLeft: '12px',\n paddingRight: '12px',\n cursor: 'pointer',\n }}\n >\n {binding.modes.map((mode) => (\n <option key={mode.value} value={mode.value} disabled={mode.enabled === false}>\n {mode.label}\n </option>\n ))}\n </select>\n ) : modeSelectionStyle === 'modal' ? (\n <>\n <div\n onClick={() => {\n setPendingMode(currentMode);\n setShowModeModal(true);\n }}\n style={{\n padding: '12px',\n backgroundColor: surfaceColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n cursor: 'pointer',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <span>\n {binding.modes.find((m) => m.value === currentMode)?.label || currentMode}\n </span>\n <span style={{ fontSize: '20px', marginLeft: '8px' }}>▼</span>\n </div>\n {showModeModal && (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.5)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 20000,\n }}\n onClick={() => {\n setShowModeModal(false);\n setPendingMode(null);\n }}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n style={{\n backgroundColor: backgroundColor,\n borderRadius: '8px',\n padding: '24px',\n minWidth: '300px',\n maxWidth: '400px',\n border: `2px solid ${borderColor}`,\n }}\n >\n <h3\n style={{\n margin: '0 0 4px 0',\n fontSize: `${fontSize.title}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n color: textColor,\n }}\n >\n Select Mode\n </h3>\n <div\n style={{\n marginBottom: '16px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n color: mutedTextColor,\n textTransform: 'uppercase',\n }}\n >\n {binding.display?.title ||\n binding.deviceName ||\n binding.tag ||\n binding.nodeId}\n </div>\n <select\n value={pendingMode || currentMode}\n onChange={(e) => setPendingMode(e.target.value)}\n style={{\n width: '100%',\n height: `${touchSize}px`,\n marginBottom: '8px',\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n paddingLeft: '12px',\n paddingRight: '12px',\n cursor: 'pointer',\n }}\n >\n {binding.modes.map((mode) => (\n <option\n key={mode.value}\n value={mode.value}\n disabled={mode.enabled === false}\n >\n {mode.label}\n </option>\n ))}\n </select>\n {(() => {\n const selectedMode = binding.modes.find(\n (m) => m.value === (pendingMode || currentMode)\n );\n return selectedMode?.description ? (\n <div\n style={{\n marginBottom: '16px',\n padding: '12px',\n backgroundColor: surfaceColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.description}px`,\n color: mutedTextColor,\n fontFamily: 'Arial, sans-serif',\n lineHeight: '1.4',\n }}\n >\n {selectedMode.description}\n </div>\n ) : null;\n })()}\n <div style={{ display: 'flex', gap: '12px' }}>\n <button\n onClick={() => {\n // Always send the displayed mode on confirm — either the\n // user's explicit selection (pendingMode) or the current\n // dropdown value (currentMode / first available mode).\n // This lets operators re-issue the same command without\n // toggling away and back.\n const modeToSend =\n pendingMode || currentMode || binding.modes[0]?.value;\n if (modeToSend) {\n handleModeChange(modeToSend);\n }\n setShowModeModal(false);\n setPendingMode(null);\n }}\n style={{\n flex: 1,\n height: `${touchSize}px`,\n backgroundColor: '#4ade80',\n color: '#000000',\n border: '2px solid #22c55e',\n borderRadius: '6px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n cursor: 'pointer',\n }}\n >\n Confirm\n </button>\n <button\n onClick={() => {\n setShowModeModal(false);\n setPendingMode(null);\n }}\n style={{\n flex: 1,\n height: `${touchSize}px`,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n cursor: 'pointer',\n }}\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n )}\n </>\n ) : (\n <div\n style={{\n display: 'flex',\n gap: spacing.gap,\n overflowX: 'auto',\n overflowY: 'hidden',\n scrollbarWidth: 'thin',\n scrollbarColor: `${borderColor} transparent`,\n paddingBottom: '4px',\n marginBottom: '-4px',\n }}\n >\n {binding.modes.map((mode) => {\n const isSelected = mode.value === currentMode;\n const isEnabled = mode.enabled !== false;\n\n return (\n <button\n key={mode.value}\n onClick={() => isEnabled && handleModeChange(mode.value)}\n disabled={!isEnabled}\n style={{\n flex: '0 0 auto',\n minWidth: '90px',\n height: touchSize,\n backgroundColor: isSelected ? selectedColor : surfaceColor,\n color: isSelected ? '#000000' : textColor,\n border: `1px solid ${isSelected ? '#b4d0fe' : borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n cursor: isEnabled ? 'pointer' : 'not-allowed',\n opacity: isEnabled ? 1 : 0.4,\n whiteSpace: 'nowrap',\n }}\n >\n {mode.label}\n </button>\n );\n })}\n </div>\n )}\n </div>\n )}\n\n {/* Parameters */}\n {currentParams.length > 0 && (\n <div style={{ marginBottom: spacing.marginBottom }}>\n <label\n style={{\n display: 'block',\n marginBottom: spacing.gap,\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n fontFamily: 'Arial, sans-serif',\n color: mutedTextColor,\n }}\n >\n Parameters\n </label>\n {currentParams.map((param) => (\n <div\n key={param.id}\n style={{\n marginBottom: spacing.gap,\n padding: spacing.itemPadding,\n backgroundColor: surfaceColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n }}\n >\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n marginBottom: spacing.gap,\n }}\n >\n <span\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n fontFamily: 'Arial, sans-serif',\n color: mutedTextColor,\n }}\n >\n {param.label}\n {param.forced?.active && (\n <span\n aria-label=\"Forced\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '16px',\n height: '16px',\n fontSize: '10px',\n fontWeight: 700,\n color: FORCED_MARK_COLOR.text,\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n borderRadius: '50%',\n }}\n >\n F\n </span>\n )}\n </span>\n <span\n style={{\n fontSize: `${fontSize.value}px`,\n fontWeight: 700,\n fontFamily: 'Arial, sans-serif',\n color: textColor,\n }}\n >\n {localParameterValues[param.id] ?? param.value}{' '}\n {param.unit && (\n <span style={{ color: mutedTextColor, fontSize: '10px', fontWeight: 600 }}>\n {param.unit}\n </span>\n )}\n </span>\n </div>\n {param.forced?.active && param.forced.realValue !== undefined && (\n <div\n style={{\n display: 'flex',\n justifyContent: 'flex-end',\n marginTop: '-4px',\n marginBottom: spacing.gap,\n fontSize: '10px',\n fontWeight: 500,\n fontFamily: 'Arial, sans-serif',\n color: mutedTextColor,\n }}\n >\n {/* A discrete output's real value is a boolean (R2: open/closed,\n on/off); render it as ON/OFF, matching DataOverlay's\n formatValue, not the literal string \"true\"/\"false\". */}\n REAL{' '}\n {typeof param.forced.realValue === 'boolean'\n ? param.forced.realValue\n ? 'ON'\n : 'OFF'\n : String(param.forced.realValue)}\n {param.unit ? ` ${param.unit}` : ''}\n </div>\n )}\n\n {param.type === 'boolean' ? (\n <button\n onClick={() => {\n const newValue = !localParameterValues[param.id];\n handleParameterInput(param.id, newValue);\n handleParameterCommit(param.id, newValue);\n }}\n disabled={param.readOnly}\n style={{\n width: '100%',\n height: touchSize,\n backgroundColor: localParameterValues[param.id] ? '#3f3f46' : surfaceColor,\n color: localParameterValues[param.id] ? textColor : mutedTextColor,\n border: `1px solid ${localParameterValues[param.id] ? '#52525b' : borderColor}`,\n borderRadius: '4px',\n fontSize: '12px',\n fontWeight: 600,\n fontFamily: 'monospace',\n letterSpacing: '0.5px',\n cursor: param.readOnly ? 'not-allowed' : 'pointer',\n transition: 'all 0.15s',\n }}\n >\n {localParameterValues[param.id] ? 'ON' : 'OFF'}\n </button>\n ) : param.type === 'select' ? (\n <select\n value={localParameterValues[param.id] || param.value}\n onChange={(e) => {\n handleParameterInput(param.id, e.target.value);\n handleParameterCommit(param.id, e.target.value);\n }}\n disabled={param.readOnly}\n style={{\n width: '100%',\n height: touchSize,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '4px',\n padding: '0 12px',\n fontSize: '12px',\n fontFamily: 'monospace',\n cursor: 'pointer',\n }}\n >\n {param.options?.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n ) : onParameterActivate && !param.readOnly ? (\n // Tappable value: defer editing to the consumer's editor (e.g. a\n // numpad dialog). Commit happens via onParameterChange.\n <button\n type=\"button\"\n onClick={() =>\n onParameterActivate(param.id, localParameterValues[param.id] ?? param.value)\n }\n style={{\n width: '100%',\n height: touchSize,\n display: 'flex',\n alignItems: 'center',\n backgroundColor: '#ffffff',\n color: textColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n padding: '0 12px',\n fontSize: `${fontSize.input}px`,\n fontFamily: 'Arial, sans-serif',\n fontWeight: 600,\n textAlign: 'left',\n cursor: 'pointer',\n }}\n >\n {localParameterValues[param.id] ?? param.value}\n {param.unit ? ` ${param.unit}` : ''}\n </button>\n ) : (\n <input\n type={param.type === 'number' ? 'number' : 'text'}\n value={localParameterValues[param.id] ?? param.value}\n onChange={(e) =>\n handleParameterInput(\n param.id,\n param.type === 'number' ? +e.target.value : e.target.value\n )\n }\n onBlur={() => handleParameterCommit(param.id, localParameterValues[param.id])}\n disabled={param.readOnly}\n min={param.min}\n max={param.max}\n step={param.step}\n style={{\n width: '100%',\n height: touchSize,\n backgroundColor: '#ffffff',\n color: textColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n padding: '0 12px',\n fontSize: `${fontSize.input}px`,\n fontFamily: 'Arial, sans-serif',\n fontWeight: 600,\n }}\n />\n )}\n </div>\n ))}\n </div>\n )}\n\n {/* Start/Stop Buttons: two fixed places, see startStopRowShown */}\n {startStopRowShown && (\n <div\n data-testid=\"start-stop-row\"\n style={{ display: 'flex', gap: spacing.gap, marginBottom: spacing.marginBottom }}\n >\n <button\n type=\"button\"\n data-control=\"start\"\n data-enabled={startEnabledNow ? 'true' : 'false'}\n onClick={() => {\n if (startEnabledNow) onStart?.();\n }}\n disabled={!startEnabledNow}\n aria-busy={startPending || undefined}\n style={startStopButtonStyle(startEnabledNow, '#10b981')}\n >\n {startPending ? 'STARTING\\u2026' : 'START'}\n </button>\n <button\n type=\"button\"\n data-control=\"stop\"\n data-enabled={stopEnabledNow ? 'true' : 'false'}\n onClick={() => {\n if (stopEnabledNow) onStop?.();\n }}\n disabled={!stopEnabledNow}\n aria-busy={stopPending || undefined}\n style={startStopButtonStyle(stopEnabledNow, '#ef4444')}\n >\n {stopPending ? 'STOPPING\\u2026' : 'STOP'}\n </button>\n </div>\n )}\n\n {/* Custom Actions */}\n {activeCapabilities?.customActions && activeCapabilities.customActions.length > 0 && (\n <div\n style={{\n display: 'flex',\n flexWrap: customActionsInRow ? 'nowrap' : 'wrap',\n gap: '8px',\n }}\n >\n {activeCapabilities.customActions.map((action) => {\n const actionDisabled = action.disabled === true;\n return (\n <button\n key={action.id}\n onClick={() => {\n if (!actionDisabled) onCustomAction?.(action.id);\n }}\n disabled={actionDisabled}\n title={actionDisabled ? action.disabledReason : undefined}\n style={{\n ...(customActionsInRow\n ? { flex: '1 1 0px', minWidth: 0, paddingLeft: '4px', paddingRight: '4px' }\n : { flex: 1, minWidth: '120px' }),\n height: touchSize,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n fontSize: '11px',\n fontWeight: 600,\n fontFamily: 'monospace',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n cursor: actionDisabled ? 'not-allowed' : 'pointer',\n opacity: actionDisabled ? 0.4 : 1,\n transition: 'all 0.15s',\n }}\n >\n {action.icon && <span style={{ marginRight: '6px' }}>{action.icon}</span>}\n {action.label}\n </button>\n );\n })}\n </div>\n )}\n </div>\n\n {/* Toast Notification for Command Result */}\n {binding.state.lastCommandResult && showToast && showBuiltInToast && (\n <div\n style={{\n position: 'fixed',\n bottom: '20px',\n ...(position === 'bottom' && align === 'right'\n ? { left: '20px', right: 'auto' } // Show on left if panel is aligned right\n : { right: '20px', left: 'auto' }), // Default to right\n maxWidth: '400px',\n padding: '12px 16px',\n backgroundColor: binding.state.lastCommandResult.success ? '#10b981' : '#ef4444',\n color: '#ffffff',\n border: 'none',\n borderRadius: '8px',\n fontSize: `${fontSize.label}px`,\n fontFamily: 'Arial, sans-serif',\n fontWeight: 600,\n boxShadow: '0 4px 12px rgba(0,0,0,0.15)',\n zIndex: 10000,\n animation:\n position === 'bottom' && align === 'right'\n ? 'slideInToastLeft 0.3s ease-out'\n : 'slideInToast 0.3s ease-out',\n }}\n >\n {binding.state.lastCommandResult.message}\n </div>\n )}\n\n <style>{`\n @keyframes slideInToast {\n from { \n opacity: 0;\n transform: translateX(20px);\n }\n to { \n opacity: 1;\n transform: translateX(0);\n }\n }\n @keyframes slideInToastLeft {\n from { \n opacity: 0;\n transform: translateX(-20px);\n }\n to { \n opacity: 1;\n transform: translateX(0);\n }\n }\n @keyframes slideUp {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n }\n @keyframes slideIn {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n `}</style>\n </>\n );\n}\n\n/** Memo boundary - see DeviceControlPanelImpl doc comment (kiosk heap-leak fix). */\nexport const DeviceControlPanel = React.memo(DeviceControlPanelImpl);\n","import React, { useState, useEffect, useCallback, useRef, useId } from 'react';\n\nexport interface NumpadDialogProps {\n /** Whether the dialog is open */\n isOpen: boolean;\n /** Called when the dialog is dismissed without applying */\n onClose: () => void;\n /** Called with the validated, precision-rounded value when the user applies */\n onApply: (value: number) => void;\n /** Device / context name shown in the header */\n deviceName?: string;\n /** Parameter label shown in the header */\n parameterLabel?: string;\n /** Engineering unit shown next to the value and in range hints */\n unit?: string;\n /** Minimum allowed value (also enables the +/- key when negative) */\n min?: number;\n /** Maximum allowed value */\n max?: number;\n /** Step (reserved for future increment controls) */\n step?: number;\n /** Decimal precision applied on commit */\n precision?: number;\n /** Current value shown as a reference above the entry field */\n currentValue?: number;\n /**\n * Optimize for touch panels (larger keys/targets). Defaults to `true` because\n * this is aimed at industrial touchscreen HMIs; set `false` for compact\n * desktop/keyboard-first use.\n */\n touchOptimized?: boolean;\n}\n\n/**\n * Modal numeric keypad for entering a device setpoint.\n *\n * Pairs with `DeviceControlPanel`'s `onParameterActivate` hook: the panel\n * renders a tappable value, the consumer opens this dialog, and `onApply`\n * commits via `onParameterChange`.\n *\n * Designed for BOTH touch and keyboard:\n * - Touch: large keys, press feedback, backdrop dismiss, body scroll-lock.\n * - Keyboard: full typing (0-9, `.`, `-`, Backspace, Enter to apply, Escape to\n * cancel), on-screen keys kept out of the Tab order, focus trap, focus-visible\n * outlines.\n *\n * Safety for setpoint use: nothing auto-commits — only Apply / Enter writes, and\n * out-of-range values are rejected (never silently clamped). The field pre-fills\n * with the current value so an operator never blind-zeroes a live setpoint.\n */\nexport const NumpadDialog: React.FC<NumpadDialogProps> = ({\n isOpen,\n onClose,\n onApply,\n deviceName = '',\n parameterLabel = 'Setpoint',\n unit = '',\n min = 0,\n max = 100,\n precision,\n currentValue = 0,\n touchOptimized = true,\n}) => {\n const [inputValue, setInputValue] = useState('');\n // \"pristine\" = the field still holds the pre-filled current value, untouched.\n // The first keystroke replaces it (standard numpad behavior) so the operator\n // sees the current setpoint on open but can type a fresh value immediately.\n const [pristine, setPristine] = useState(true);\n const dialogRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n const decimals = precision ?? 1;\n\n // On open, pre-fill with the current value (at the dialog's precision) so it's\n // the prominent number and matches what Apply would commit.\n useEffect(() => {\n if (isOpen) {\n setInputValue(Number(currentValue).toFixed(decimals));\n setPristine(true);\n }\n }, [isOpen, currentValue, decimals]);\n\n // Move focus into the dialog on open and restore it to the trigger on close.\n useEffect(() => {\n if (!isOpen) return;\n const previouslyFocused = document.activeElement as HTMLElement | null;\n dialogRef.current?.focus();\n return () => previouslyFocused?.focus?.();\n }, [isOpen]);\n\n // Lock background scroll so a touch drag can't scroll the diagram behind us.\n useEffect(() => {\n if (!isOpen) return;\n const prevOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n return () => {\n document.body.style.overflow = prevOverflow;\n };\n }, [isOpen]);\n\n const handleApply = useCallback(() => {\n const n = Number(inputValue);\n if (inputValue === '' || inputValue === '-' || inputValue === '.' || inputValue === '-.') return;\n if (isNaN(n) || n < min || n > max) return;\n onApply(Number(n.toFixed(decimals)));\n }, [inputValue, min, max, onApply, decimals]);\n\n const appendDigit = useCallback(\n (digit: string) => {\n setInputValue((prev) => {\n // First keystroke after open replaces the pre-filled current value.\n const base = pristine ? '' : prev;\n if (digit === '.') {\n // No decimal point allowed when precision is 0 (integer-only).\n if (decimals === 0) return base;\n if (base.includes('.')) return base;\n if (base === '' || base === '-') return base + '0.';\n return base + '.';\n }\n // Cap decimal places at `precision` so the on-screen value is exactly\n // what Apply commits — no surprise rounding/truncation on commit.\n const dot = base.indexOf('.');\n if (dot !== -1 && base.length - dot - 1 >= decimals) return base;\n // Replace a lone leading zero (except when starting a decimal)\n if (base === '0') return digit;\n if (base === '-0') return '-' + digit;\n return base + digit;\n });\n if (pristine) setPristine(false);\n },\n [pristine, decimals]\n );\n\n const handleBackspace = useCallback(() => {\n setPristine(false);\n setInputValue((prev) => prev.slice(0, -1));\n }, []);\n\n const handleClear = useCallback(() => {\n setPristine(false);\n setInputValue('');\n }, []);\n\n const handleToggleSign = useCallback(() => {\n if (min >= 0) return;\n setPristine(false);\n setInputValue((prev) => {\n if (prev.startsWith('-')) return prev.slice(1);\n if (prev === '' || prev === '0') return '-';\n return '-' + prev;\n });\n }, [min]);\n\n // Long-press Backspace to clear (touch convenience). A short tap backspaces;\n // holding ~500ms clears the whole entry.\n const bsTimer = useRef<number | null>(null);\n const bsLongFired = useRef(false);\n const clearBsTimer = () => {\n if (bsTimer.current !== null) {\n clearTimeout(bsTimer.current);\n bsTimer.current = null;\n }\n };\n const handleBackspacePointerDown = () => {\n bsLongFired.current = false;\n bsTimer.current = window.setTimeout(() => {\n bsLongFired.current = true;\n handleClear();\n }, 500);\n };\n const handleBackspaceClick = () => {\n clearBsTimer();\n if (bsLongFired.current) {\n bsLongFired.current = false;\n return; // long-press already cleared; don't also backspace\n }\n handleBackspace();\n };\n useEffect(() => () => clearBsTimer(), []);\n\n // Keyboard support + focus trap + Enter-guard.\n useEffect(() => {\n if (!isOpen) return;\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Tab') {\n const root = dialogRef.current;\n if (!root) return;\n // On-screen keys are tabIndex=-1, so only the actions (Cancel/Apply) are\n // tabbable. Cycle focus within the dialog.\n const focusable = Array.from(\n root.querySelectorAll<HTMLElement>('button:not([disabled])')\n ).filter((el) => el.tabIndex !== -1);\n if (focusable.length === 0) {\n e.preventDefault();\n root.focus();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement as HTMLElement | null;\n if (e.shiftKey) {\n if (active === first || !root.contains(active)) {\n e.preventDefault();\n last.focus();\n }\n } else if (active === last || !root.contains(active)) {\n e.preventDefault();\n first.focus();\n }\n return;\n }\n if (e.key === 'Enter') {\n // If a button is focused, let its native click handle Enter (avoids a\n // double Apply/Cancel). Otherwise apply.\n const active = document.activeElement as HTMLElement | null;\n if (active && active.tagName === 'BUTTON' && dialogRef.current?.contains(active)) return;\n e.preventDefault();\n handleApply();\n } else if (e.key === 'Escape') {\n e.preventDefault();\n onClose();\n } else if (e.key === 'Backspace') {\n e.preventDefault();\n handleBackspace();\n } else if (/^[0-9]$/.test(e.key)) {\n e.preventDefault();\n appendDigit(e.key);\n } else if (e.key === '.') {\n e.preventDefault();\n appendDigit('.');\n } else if (e.key === '-') {\n e.preventDefault();\n handleToggleSign();\n }\n };\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [isOpen, handleApply, onClose, handleBackspace, appendDigit, handleToggleSign]);\n\n if (!isOpen) return null;\n\n const parsed = Number(inputValue);\n const hasNumber =\n inputValue !== '' &&\n inputValue !== '-' &&\n inputValue !== '.' &&\n inputValue !== '-.' &&\n !isNaN(parsed);\n // Live range check so the operator sees WHY a value is rejected while typing,\n // not only after pressing Apply.\n const belowMin = hasNumber && parsed < min;\n const aboveMax = hasNumber && parsed > max;\n const outOfRange = belowMin || aboveMax;\n const isValid = hasNumber && !outOfRange;\n\n const alarmColor = '#dc2626';\n const limitStyle = (violated: boolean): React.CSSProperties =>\n violated\n ? { color: alarmColor, fontWeight: 700, background: '#fee2e2', borderRadius: 3, padding: '0 3px' }\n : {};\n\n // Touch-first sizing.\n const keySize = touchOptimized ? 60 : 46;\n const keyFont = touchOptimized ? 22 : 18;\n const actionSize = touchOptimized ? 52 : 42;\n\n const keyStyle: React.CSSProperties = {\n minWidth: 52,\n minHeight: keySize,\n fontSize: keyFont,\n fontWeight: 600,\n borderRadius: 8,\n border: '1px solid #e2e8f0',\n background: '#ffffff',\n color: '#0f172a',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n userSelect: 'none',\n boxShadow: '0 1px 2px rgba(15, 23, 42, 0.06)',\n };\n\n const utilKeyStyle: React.CSSProperties = {\n flex: 1,\n minHeight: touchOptimized ? 44 : 38,\n fontSize: 13,\n fontWeight: 600,\n borderRadius: 8,\n border: '1px solid #e2e8f0',\n background: '#f8fafc',\n color: '#64748b',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n userSelect: 'none',\n };\n\n const reason = belowMin\n ? `Below minimum of ${min} ${unit}`\n : aboveMax\n ? `Above maximum of ${max} ${unit}`\n : 'Type or tap to change the value';\n\n // Backspace lives in the number grid (bottom-left) when negatives aren't\n // allowed; otherwise that cell is the +/- key and Backspace moves to the\n // utility row. Either way there is exactly one Backspace and no empty cell.\n const backspaceInGrid = min >= 0;\n const BackspaceButton = ({ style, label }: { style: React.CSSProperties; label: string }) => (\n <button\n type=\"button\"\n className=\"np-key\"\n tabIndex={-1}\n aria-label=\"Backspace\"\n style={style}\n onPointerDown={handleBackspacePointerDown}\n onPointerUp={clearBsTimer}\n onPointerLeave={clearBsTimer}\n onClick={handleBackspaceClick}\n >\n {label}\n </button>\n );\n\n return (\n // Backdrop\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(15, 23, 42, 0.4)',\n zIndex: 10000,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n onClick={onClose}\n >\n <style>{`\n .np-key { transition: background .1s, transform .05s; touch-action: manipulation; }\n .np-key:active:not(:disabled) { background:#e2e8f0; transform: translateY(1px); }\n .np-key:focus-visible, .np-action:focus-visible { outline: 3px solid #2563eb; outline-offset: 2px; }\n @keyframes np-blink { 0%,49%{opacity:1} 50%,100%{opacity:0} }\n .np-caret { display:inline-block; width:2px; height:1.1em; background:#2563eb; animation: np-blink 1s step-end infinite; }\n `}</style>\n {/* Modal */}\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n tabIndex={-1}\n style={{\n width: touchOptimized ? '340px' : '300px',\n maxHeight: '90vh',\n overflow: 'auto',\n background: '#ffffff',\n borderRadius: 12,\n border: '1px solid #e2e8f0',\n boxShadow: '0 10px 40px rgba(15, 23, 42, 0.2)',\n fontFamily: \"'Inter', system-ui, sans-serif\",\n outline: 'none',\n }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div style={{ padding: '14px 16px 0 16px' }}>\n <h3 id={titleId} style={{ margin: 0, fontSize: 15, fontWeight: 700, color: '#0f172a' }}>\n {deviceName ? `${deviceName} - ${parameterLabel}` : parameterLabel}\n </h3>\n </div>\n\n <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 10 }}>\n {/* Current value + range */}\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n fontSize: 12,\n color: '#64748b',\n }}\n >\n <span>\n Current: {currentValue} {unit}\n </span>\n <span>\n Range: <span style={limitStyle(belowMin)}>{min}</span>-\n <span style={limitStyle(aboveMax)}>{max}</span> {unit}\n </span>\n </div>\n\n {/* Input display — the entered value turns red when out of range */}\n <div\n aria-live=\"polite\"\n aria-atomic=\"true\"\n aria-label={`Entered value ${inputValue || '0'} ${unit}`}\n style={{\n background: outOfRange ? '#fef2f2' : '#f8fafc',\n borderRadius: 8,\n padding: '10px 14px',\n fontSize: 28,\n fontWeight: 700,\n textAlign: 'right',\n color: outOfRange ? alarmColor : '#0f172a',\n minHeight: 48,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n gap: 6,\n border: outOfRange ? '1.5px solid #fca5a5' : '1.5px solid #e2e8f0',\n }}\n >\n <span>{inputValue || '0'}</span>\n <span className=\"np-caret\" aria-hidden=\"true\" />\n <span style={{ fontSize: 14, color: '#94a3b8', fontWeight: 500 }}>{unit}</span>\n </div>\n\n {/* Reason / hint message (announced to screen readers) */}\n <div\n role=\"status\"\n aria-live=\"polite\"\n style={{\n fontSize: 12,\n color: outOfRange ? alarmColor : '#94a3b8',\n fontWeight: outOfRange ? 600 : 400,\n textAlign: 'right',\n minHeight: 16,\n }}\n >\n {reason}\n </div>\n\n {/* Numberpad — keys are out of the Tab order (keyboard users type digits) */}\n <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 5 }}>\n {['7', '8', '9', '4', '5', '6', '1', '2', '3'].map((digit) => (\n <button\n key={digit}\n type=\"button\"\n className=\"np-key\"\n tabIndex={-1}\n style={keyStyle}\n onClick={() => appendDigit(digit)}\n >\n {digit}\n </button>\n ))}\n {/* Bottom-left cell: +/- when negatives are allowed, else Backspace */}\n {min < 0 ? (\n <button\n type=\"button\"\n className=\"np-key\"\n tabIndex={-1}\n aria-label=\"Toggle sign\"\n style={{ ...keyStyle, fontSize: 16, background: '#f8fafc', color: '#64748b' }}\n onClick={handleToggleSign}\n >\n +/−\n </button>\n ) : (\n <BackspaceButton style={{ ...keyStyle, fontSize: 20, background: '#f8fafc', color: '#64748b' }} label=\"⌫\" />\n )}\n <button type=\"button\" className=\"np-key\" tabIndex={-1} style={keyStyle} onClick={() => appendDigit('0')}>\n 0\n </button>\n <button\n type=\"button\"\n className=\"np-key\"\n tabIndex={-1}\n aria-label=\"Decimal point\"\n style={{\n ...keyStyle,\n cursor: decimals === 0 ? 'not-allowed' : 'pointer',\n opacity: decimals === 0 ? 0.4 : 1,\n }}\n disabled={decimals === 0}\n onClick={() => appendDigit('.')}\n >\n .\n </button>\n </div>\n\n {/* Utility row: Clear (+ Backspace only when it's not in the grid) */}\n <div style={{ display: 'flex', gap: 5 }}>\n <button type=\"button\" className=\"np-key\" tabIndex={-1} style={utilKeyStyle} onClick={handleClear}>\n Clear\n </button>\n {!backspaceInGrid && <BackspaceButton style={utilKeyStyle} label=\"Backspace\" />}\n </div>\n\n {/* Action buttons (the only Tab stops in the dialog) */}\n <div style={{ display: 'flex', gap: 8, marginTop: 2 }}>\n <button\n type=\"button\"\n className=\"np-action\"\n onClick={onClose}\n style={{\n flex: 1,\n minHeight: actionSize,\n borderRadius: 8,\n border: '1px solid #e2e8f0',\n background: '#ffffff',\n color: '#0f172a',\n fontSize: 14,\n fontWeight: 600,\n cursor: 'pointer',\n }}\n >\n Cancel\n </button>\n <button\n type=\"button\"\n className=\"np-action\"\n onClick={handleApply}\n disabled={!isValid}\n aria-label={isValid ? `Apply ${inputValue} ${unit}` : 'Apply (enter a valid value)'}\n style={{\n flex: 1,\n minHeight: actionSize,\n borderRadius: 8,\n border: 'none',\n background: '#2563eb',\n color: '#ffffff',\n fontSize: 14,\n fontWeight: 600,\n cursor: isValid ? 'pointer' : 'not-allowed',\n opacity: isValid ? 1 : 0.4,\n }}\n >\n Apply\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n};\n","import React from 'react';\nimport { Tab } from '@mui/material';\nimport {\n ChevronLeft,\n ChevronRight,\n DisplayModeToggle,\n FixedSVGContainer,\n FooterPanel,\n FullscreenContainer,\n LeftPanel,\n LeftToggleButton,\n PanelContent,\n PanelTabs,\n RightPanel,\n RightToggleButton,\n SVGArea,\n ScrollableSVGWrapper,\n ZoomButton,\n ZoomControls,\n} from './FullscreenWorkspace.styles';\nimport type { DisplayMode, FullscreenWorkspaceProps } from './types';\n\nexport function FullscreenWorkspace({\n svgContent,\n overlays,\n leftPanel,\n rightPanel,\n footer,\n tabs,\n activeTab,\n onTabChange,\n defaultLeftCollapsed = false,\n defaultRightCollapsed = false,\n leftCollapsed: leftCollapsedProp,\n rightCollapsed: rightCollapsedProp,\n onLeftCollapseChange,\n onRightCollapseChange,\n displayMode,\n defaultDisplayMode = 'standard',\n onDisplayModeChange,\n showDisplayModeToggle = true,\n showZoomControls = false,\n onZoomIn,\n onZoomOut,\n onResetZoom,\n zoomDisabled = false,\n className = '',\n ...rest\n}: FullscreenWorkspaceProps): React.ReactElement {\n const [leftCollapsedState, setLeftCollapsedState] = React.useState(defaultLeftCollapsed);\n const [rightCollapsedState, setRightCollapsedState] = React.useState(defaultRightCollapsed);\n const [internalDisplayMode, setInternalDisplayMode] =\n React.useState<DisplayMode>(displayMode ?? defaultDisplayMode);\n const [internalTab, setInternalTab] = React.useState<string>(\n tabs && tabs.length > 0 ? tabs[0].value : ''\n );\n\n const leftCollapsed = leftCollapsedProp ?? leftCollapsedState;\n const rightCollapsed = rightCollapsedProp ?? rightCollapsedState;\n const isDisplayModeControlled = displayMode !== undefined;\n const currentDisplayMode = isDisplayModeControlled ? displayMode : internalDisplayMode;\n const isTabControlled = activeTab !== undefined && activeTab !== null;\n\n const resolvedTabs = React.useMemo(() => tabs ?? [], [tabs]);\n const defaultTabValue = resolvedTabs.length > 0 ? resolvedTabs[0].value : '';\n const currentTab = isTabControlled ? (activeTab as string) : internalTab || defaultTabValue;\n\n React.useEffect(() => {\n if (resolvedTabs.length > 0) {\n const values = resolvedTabs.map((tab) => tab.value);\n if (!values.includes(currentTab)) {\n const fallback = activeTab ?? defaultTabValue;\n setInternalTab(fallback);\n }\n }\n }, [resolvedTabs, activeTab, currentTab, defaultTabValue]);\n\n React.useEffect(() => {\n if (displayMode !== undefined) {\n setInternalDisplayMode(displayMode);\n }\n }, [displayMode]);\n\n const handleLeftToggle = () => {\n const next = !leftCollapsed;\n if (leftCollapsedProp === undefined) {\n setLeftCollapsedState(next);\n }\n onLeftCollapseChange?.(next);\n };\n\n const handleRightToggle = () => {\n const next = !rightCollapsed;\n if (rightCollapsedProp === undefined) {\n setRightCollapsedState(next);\n }\n onRightCollapseChange?.(next);\n };\n\n const handleModeToggle = () => {\n const nextMode = currentDisplayMode === 'standard' ? 'dashboard' : 'standard';\n if (!isDisplayModeControlled) {\n setInternalDisplayMode(nextMode);\n }\n onDisplayModeChange?.(nextMode);\n };\n\n const handleTabChangeInternal = (_: React.SyntheticEvent, value: string) => {\n if (!isTabControlled) {\n setInternalTab(value);\n }\n onTabChange?.(value);\n };\n\n const currentTabContent =\n resolvedTabs.find((tab) => tab.value === currentTab)?.content ?? null;\n\n const showLeftToggle = Boolean(leftPanel);\n const showRightToggle = Boolean(rightPanel);\n\n return (\n <FullscreenContainer\n leftCollapsed={leftCollapsed}\n rightCollapsed={rightCollapsed}\n className={`tff-svg-container ${className}`.trim()}\n {...rest}\n >\n {showLeftToggle && (\n <LeftToggleButton\n collapsed={leftCollapsed}\n onClick={handleLeftToggle}\n aria-label={leftCollapsed ? 'Expand left panel' : 'Collapse left panel'}\n >\n {leftCollapsed ? <ChevronRight /> : <ChevronLeft />}\n </LeftToggleButton>\n )}\n\n {showRightToggle && (\n <RightToggleButton\n collapsed={rightCollapsed}\n onClick={handleRightToggle}\n aria-label={rightCollapsed ? 'Expand right panel' : 'Collapse right panel'}\n >\n {rightCollapsed ? <ChevronLeft /> : <ChevronRight />}\n </RightToggleButton>\n )}\n\n {showDisplayModeToggle && (\n <DisplayModeToggle\n mode={currentDisplayMode}\n onClick={handleModeToggle}\n aria-label=\"Toggle display mode\"\n >\n {currentDisplayMode === 'dashboard' ? 'Dashboard Mode' : 'Standard Mode'}\n </DisplayModeToggle>\n )}\n\n <SVGArea>\n {showZoomControls && (\n <ZoomControls>\n <ZoomButton\n onClick={onZoomIn}\n disabled={zoomDisabled || !onZoomIn}\n size=\"small\"\n aria-label=\"Zoom in\"\n >\n +\n </ZoomButton>\n <ZoomButton\n onClick={onZoomOut}\n disabled={zoomDisabled || !onZoomOut}\n size=\"small\"\n aria-label=\"Zoom out\"\n >\n -\n </ZoomButton>\n {onResetZoom && (\n <ZoomButton\n onClick={onResetZoom}\n disabled={zoomDisabled}\n size=\"small\"\n aria-label=\"Reset zoom\"\n >\n Reset\n </ZoomButton>\n )}\n </ZoomControls>\n )}\n\n <ScrollableSVGWrapper className=\"svg-scroll-wrapper\">\n <FixedSVGContainer className=\"svg-container\">\n {svgContent}\n {overlays}\n </FixedSVGContainer>\n </ScrollableSVGWrapper>\n </SVGArea>\n\n <LeftPanel collapsed={leftCollapsed}>\n {resolvedTabs.length > 0 ? (\n <>\n <PanelTabs\n value={currentTab}\n onChange={handleTabChangeInternal}\n aria-label=\"Left panel tabs\"\n >\n {resolvedTabs.map((tab) => (\n <Tab key={tab.value} label={tab.label} value={tab.value} />\n ))}\n </PanelTabs>\n <PanelContent>{currentTabContent}</PanelContent>\n </>\n ) : (\n leftPanel\n )}\n </LeftPanel>\n\n <RightPanel collapsed={rightCollapsed}>{rightPanel}</RightPanel>\n\n <FooterPanel>{footer}</FooterPanel>\n </FullscreenContainer>\n );\n}\n\nexport type {\n DisplayMode,\n FullscreenContainerProps,\n FullscreenWorkspaceProps,\n FullscreenWorkspaceTab,\n OverlayTheme,\n SVGLockedOverlayProps,\n TrendLineProps,\n CircularGaugeProps,\n SparklineProps,\n EquipmentIndicatorProps,\n EquipmentIndicatorWithSparklineProps,\n} from './types';\n\nexport {\n FullscreenContainer,\n LeftPanel,\n RightPanel,\n SVGArea,\n LeftToggleButton,\n RightToggleButton,\n DisplayModeToggle,\n ZoomControls,\n ZoomButton,\n FooterPanel,\n PanelTabs,\n PanelContent,\n FixedSVGContainer,\n ScrollableSVGWrapper,\n ChevronLeft,\n ChevronRight,\n} from './FullscreenWorkspace.styles';\n\nexport {\n overlayStyles,\n SVGLockedOverlay,\n DashboardCard,\n CardHeader,\n CardValue,\n CardUnit,\n} from './FullscreenWorkspace.overlays';\n\nexport {\n TrendLine,\n Sparkline,\n CircularGauge,\n EquipmentIndicator,\n EquipmentIndicatorWithSparkline,\n} from './FullscreenWorkspace.visuals';\n","import { Box, IconButton, Tabs } from '@mui/material';\nimport styled from '@emotion/styled';\nimport type { DisplayMode, FullscreenContainerProps } from './types';\n\nexport const FullscreenContainer = styled(Box)<FullscreenContainerProps>(\n ({ leftCollapsed, rightCollapsed }) => {\n const getGridTemplateAreas = () => {\n if (leftCollapsed && rightCollapsed) {\n return `\"svg-area\" \"footer\"`;\n }\n if (leftCollapsed) {\n return `\"svg-area right-panel\" \"footer footer\"`;\n }\n if (rightCollapsed) {\n return `\"left-panel svg-area\" \"footer footer\"`;\n }\n return `\"left-panel svg-area right-panel\" \"footer footer footer\"`;\n };\n\n const getGridTemplateColumns = () => {\n if (leftCollapsed && rightCollapsed) {\n return '1fr';\n }\n if (leftCollapsed) {\n return '1fr 380px';\n }\n if (rightCollapsed) {\n return '420px 1fr';\n }\n return '420px 1fr 380px';\n };\n\n return {\n width: '100vw',\n height: '100vh',\n background: '#f5f5f5',\n display: 'grid',\n gridTemplateAreas: getGridTemplateAreas(),\n gridTemplateRows: '1fr 100px',\n gridTemplateColumns: getGridTemplateColumns(),\n gap: '10px',\n padding: '10px',\n boxSizing: 'border-box',\n transition: 'grid-template-columns 0.3s ease-in-out',\n };\n }\n);\n\nexport const LeftPanel = styled(Box)<{ collapsed: boolean }>(({ collapsed }) => ({\n gridArea: 'left-panel',\n background: 'rgba(255, 255, 255, 0.0)',\n border: '1px solid #ddd',\n borderRadius: '8px',\n padding: collapsed ? '0' : '15px',\n overflowY: 'auto',\n display: collapsed ? 'none' : 'block',\n transition: 'all 0.3s ease-in-out',\n}));\n\nexport const RightPanel = styled(Box)<{ collapsed: boolean }>(({ collapsed }) => ({\n gridArea: 'right-panel',\n background: 'rgba(255, 255, 255, 0.0)',\n border: '1px solid #ddd',\n borderRadius: '8px',\n padding: collapsed ? '0' : '15px',\n overflowY: 'auto',\n display: collapsed ? 'none' : 'block',\n transition: 'all 0.3s ease-in-out',\n}));\n\nexport const SVGArea = styled(Box)({\n gridArea: 'svg-area',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-start',\n position: 'relative',\n background: 'rgba(255, 255, 255, 0.0)',\n border: '1px solid #ddd',\n borderRadius: '8px',\n overflow: 'auto',\n width: '100%',\n height: '100%',\n});\n\nexport const LeftToggleButton = styled(IconButton)<{ collapsed: boolean }>(\n ({ collapsed }) => ({\n position: 'absolute',\n left: collapsed ? '10px' : '410px',\n top: '50%',\n transform: 'translateY(-50%)',\n zIndex: 1000,\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n border: '1px solid #ddd',\n transition: 'left 0.3s ease-in-out',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 1)',\n },\n })\n);\n\nexport const RightToggleButton = styled(IconButton)<{ collapsed: boolean }>(\n ({ collapsed }) => ({\n position: 'absolute',\n right: collapsed ? '10px' : '370px',\n top: '50%',\n transform: 'translateY(-50%)',\n zIndex: 1000,\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n border: '1px solid #ddd',\n transition: 'right 0.3s ease-in-out',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 1)',\n },\n })\n);\n\nexport const DisplayModeToggle = styled(IconButton)<{ mode: DisplayMode }>(({ mode }) => ({\n position: 'absolute',\n top: '50px',\n left: '75%',\n transform: 'translateX(-50%)',\n zIndex: 1000,\n backgroundColor:\n mode === 'dashboard' ? 'rgba(52, 152, 219, 0.9)' : 'rgba(255, 255, 255, 0.9)',\n color: mode === 'dashboard' ? 'white' : '#333',\n border: '1px solid #ddd',\n borderRadius: '20px',\n padding: '8px 16px',\n fontSize: '12px',\n fontWeight: '600',\n minWidth: '120px',\n transition: 'all 0.3s ease-in-out',\n '&:hover': {\n backgroundColor:\n mode === 'dashboard' ? 'rgba(52, 152, 219, 1)' : 'rgba(255, 255, 255, 1)',\n transform: 'translateX(-50%) scale(1.05)',\n },\n}));\n\nexport const ChevronLeft = () => (\n <div\n style={{\n width: 0,\n height: 0,\n borderTop: '6px solid transparent',\n borderBottom: '6px solid transparent',\n borderRight: '8px solid #666',\n }}\n />\n);\n\nexport const ChevronRight = () => (\n <div\n style={{\n width: 0,\n height: 0,\n borderTop: '6px solid transparent',\n borderBottom: '6px solid transparent',\n borderLeft: '8px solid #666',\n }}\n />\n);\n\nexport const ZoomControls = styled(Box)({\n position: 'absolute',\n top: '10px',\n right: '10px',\n display: 'flex',\n flexDirection: 'column',\n gap: '5px',\n zIndex: 1000,\n});\n\nexport const ZoomButton = styled(IconButton)({\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n border: '1px solid #ddd',\n width: '32px',\n height: '32px',\n fontSize: '14px',\n fontWeight: 'bold',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 1)',\n },\n});\n\nexport const FooterPanel = styled(Box)({\n gridArea: 'footer',\n background: 'rgba(255, 255, 255, 0.95)',\n border: '1px solid #ddd',\n borderRadius: '8px',\n padding: '10px',\n display: 'flex',\n justifyContent: 'space-around',\n alignItems: 'center',\n});\n\nexport const PanelTabs = styled(Tabs)({\n minHeight: '36px',\n '& .MuiTabs-indicator': {\n backgroundColor: '#546e7a',\n },\n '& .MuiTab-root': {\n minHeight: '36px',\n padding: '6px 12px',\n fontSize: '12px',\n fontWeight: '600',\n textTransform: 'none',\n color: '#666',\n '&.Mui-selected': {\n color: '#546e7a',\n },\n },\n});\n\nexport const PanelContent = styled(Box)({\n flex: 1,\n overflow: 'auto',\n padding: '10px',\n});\n\nexport const FixedSVGContainer = styled('div')({\n position: 'relative',\n width: '1300px',\n height: '400px',\n minWidth: '1300px',\n minHeight: '400px',\n maxWidth: '1300px',\n maxHeight: '400px',\n border: 'none',\n backgroundColor: 'transparent',\n overflow: 'visible',\n '& .svg-locked-overlay': {\n position: 'absolute',\n pointerEvents: 'auto',\n zIndex: 10,\n },\n '& svg': {\n position: 'absolute',\n left: 0,\n top: 0,\n width: '1300px',\n height: '280px',\n minWidth: '1300px',\n minHeight: '280px',\n maxWidth: 'none',\n maxHeight: 'none',\n objectFit: 'none',\n transform: 'none',\n border: 'none',\n },\n});\n\nexport const ScrollableSVGWrapper = styled('div')({\n position: 'relative',\n width: '100%',\n height: '100%',\n overflow: 'auto',\n transform: 'rotateX(180deg)',\n padding: '250px',\n paddingTop: '200px',\n boxSizing: 'border-box',\n '& > *': {\n display: 'block',\n transform: 'rotateX(180deg)',\n },\n});\n","import { useEffect, useState } from 'react';\nimport { Box } from '@mui/material';\nimport styled from '@emotion/styled';\nimport type { OverlayTheme, SVGLockedOverlayProps } from './types';\n\nconst baseOverlayStyles = {\n tank: {\n background:\n 'linear-gradient(135deg, rgba(69, 90, 120, 0.95) 0%, rgba(52, 73, 94, 0.95) 100%)',\n border: '1px solid #455a64',\n color: 'white',\n borderRadius: '4px',\n fontSize: '12px',\n fontWeight: '500',\n textAlign: 'center' as const,\n padding: '8px',\n minWidth: '60px',\n boxShadow: '0 1px 4px rgba(69, 90, 120, 0.2)',\n },\n pressure: {\n background:\n 'linear-gradient(135deg, rgba(30, 50, 90, 0.75) 0%, rgba(20, 35, 70, 0.75) 100%)',\n border: '1px solid #1e3a8a',\n color: 'white',\n borderRadius: '4px',\n fontSize: '11px',\n fontWeight: '500',\n textAlign: 'center' as const,\n padding: '6px',\n minWidth: '50px',\n boxShadow: '0 1px 4px rgba(30, 50, 90, 0.15)',\n },\n vessel: {\n background:\n 'linear-gradient(135deg, rgba(84, 110, 122, 0.95) 0%, rgba(69, 90, 100, 0.95) 100%)',\n border: '1px solid #546e7a',\n color: 'white',\n borderRadius: '4px',\n fontSize: '13px',\n fontWeight: '500',\n textAlign: 'center' as const,\n padding: '8px',\n minWidth: '70px',\n boxShadow: '0 1px 4px rgba(84, 110, 122, 0.2)',\n },\n flow: {\n background:\n 'linear-gradient(135deg, rgba(102, 125, 102, 0.95) 0%, rgba(85, 107, 85, 0.95) 100%)',\n border: '1px solid #689f38',\n color: 'white',\n borderRadius: '4px',\n fontSize: '11px',\n fontWeight: '500',\n textAlign: 'center' as const,\n padding: '5px',\n minWidth: '60px',\n boxShadow: '0 1px 4px rgba(102, 125, 102, 0.2)',\n },\n} as const;\n\nexport const overlayStyles: Record<OverlayTheme, typeof baseOverlayStyles[OverlayTheme]> =\n baseOverlayStyles;\n\nexport const SVGLockedOverlay = ({\n svgX,\n svgY,\n children,\n theme,\n style = {},\n className = '',\n onClick,\n containerSelector = '.tff-svg-container',\n}: SVGLockedOverlayProps) => {\n const [svgOffset, setSvgOffset] = useState({ x: 0, y: 0 });\n\n useEffect(() => {\n const updateSvgOffset = () => {\n const containerElement = document.querySelector(containerSelector);\n const svgElement = containerElement?.querySelector('svg');\n\n if (svgElement && containerElement) {\n const svgRect = svgElement.getBoundingClientRect();\n const containerRect = containerElement.getBoundingClientRect();\n\n setSvgOffset({\n x: svgRect.left - containerRect.left,\n y: svgRect.top - containerRect.top,\n });\n }\n };\n\n updateSvgOffset();\n window.addEventListener('resize', updateSvgOffset);\n return () => window.removeEventListener('resize', updateSvgOffset);\n }, [containerSelector]);\n\n const pixelX = svgX + svgOffset.x;\n const pixelY = svgY + svgOffset.y;\n\n const appliedStyle = theme ? { ...overlayStyles[theme], ...style } : style;\n\n return (\n <Box\n className={`svg-locked-overlay ${className}`.trim()}\n onClick={onClick}\n sx={{\n position: 'absolute',\n left: `${pixelX}px`,\n top: `${pixelY}px`,\n cursor: onClick ? 'pointer' : 'default',\n zIndex: 10,\n pointerEvents: 'auto',\n ...appliedStyle,\n }}\n >\n {children}\n </Box>\n );\n};\n\nexport const DashboardCard = styled(Box)<{ svgX: number; svgY: number; cardType?: string }>(\n ({ svgX, svgY, cardType }) => ({\n position: 'absolute',\n left: svgX,\n top: svgY,\n background:\n cardType === 'critical'\n ? 'linear-gradient(135deg, rgba(220, 53, 69, 0.95) 0%, rgba(173, 20, 87, 0.95) 100%)'\n : cardType === 'warning'\n ? 'linear-gradient(135deg, rgba(255, 193, 7, 0.95) 0%, rgba(255, 143, 0, 0.95) 100%)'\n : cardType === 'vessel'\n ? 'linear-gradient(135deg, rgba(46, 204, 113, 0.95) 0%, rgba(39, 174, 96, 0.95) 100%)'\n : cardType === 'process'\n ? 'linear-gradient(135deg, rgba(155, 89, 182, 0.95) 0%, rgba(142, 68, 173, 0.95) 100%)'\n : cardType === 'control'\n ? 'linear-gradient(135deg, rgba(26, 188, 156, 0.95) 0%, rgba(22, 160, 133, 0.95) 100%)'\n : 'linear-gradient(135deg, rgba(52, 152, 219, 0.95) 0%, rgba(41, 128, 185, 0.95) 100%)',\n border: '1px solid rgba(255, 255, 255, 0.2)',\n borderRadius: '12px',\n padding: '12px',\n minWidth: '140px',\n boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',\n backdropFilter: 'blur(10px)',\n color: 'white',\n fontSize: '12px',\n fontWeight: '600',\n textAlign: 'center',\n transform: 'translate(-50%, -50%)',\n transition: 'all 0.3s ease-in-out',\n cursor: 'pointer',\n '&:hover': {\n transform: 'translate(-50%, -50%) scale(1.05)',\n boxShadow: '0 12px 40px rgba(0, 0, 0, 0.4)',\n },\n })\n);\n\nexport const CardHeader = styled('div')<{ color?: string }>(({ color }) => ({\n fontSize: '10px',\n opacity: 0.9,\n marginBottom: '4px',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n color: color || 'inherit',\n borderBottom: color ? `1px solid ${color}40` : 'none',\n paddingBottom: color ? '2px' : '0',\n}));\n\nexport const CardValue = styled('div')({\n fontSize: '16px',\n fontWeight: '700',\n marginBottom: '2px',\n});\n\nexport const CardUnit = styled('div')({\n fontSize: '9px',\n opacity: 0.8,\n});\n\n","import type { CSSProperties } from 'react';\nimport type {\n CircularGaugeProps,\n EquipmentIndicatorProps,\n EquipmentIndicatorWithSparklineProps,\n SparklineProps,\n TrendLineProps,\n} from './types';\nimport { SVGLockedOverlay } from './FullscreenWorkspace.overlays';\nimport { FORCED_MARK_COLOR } from '../../../theme/statusColors';\n\nconst forcedTagStyle: CSSProperties = {\n position: 'absolute',\n top: '-6px',\n right: '-6px',\n width: '14px',\n height: '14px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '9px',\n fontWeight: 700,\n color: FORCED_MARK_COLOR.text,\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n borderRadius: '50%',\n pointerEvents: 'none',\n};\n\nexport const TrendLine = ({\n values,\n width = 60,\n height = 20,\n color = '#4fc3f7',\n strokeWidth = 1.5,\n backgroundColor = 'rgba(255, 255, 255, 0.1)',\n forced = false,\n}: TrendLineProps) => {\n if (!values || values.length < 2) {\n return (\n <div\n style={{\n position: 'relative',\n width,\n height,\n backgroundColor,\n borderRadius: '2px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <span style={{ fontSize: '8px', color: '#ccc' }}>No data</span>\n {forced && (\n <span style={forcedTagStyle} aria-label=\"Forced\">\n F\n </span>\n )}\n </div>\n );\n }\n\n const minValue = Math.min(...values);\n const maxValue = Math.max(...values);\n const range = maxValue - minValue;\n\n const normalizedValues =\n range === 0\n ? values.map(() => height / 2)\n : values.map((v) => height - 4 - ((v - minValue) / range) * (height - 8));\n\n const pathData = normalizedValues\n .map((y, i) => {\n const x = 4 + (i / (values.length - 1)) * (width - 8);\n return i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;\n })\n .join(' ');\n\n return (\n <div\n style={{\n width,\n height,\n backgroundColor,\n borderRadius: '2px',\n position: 'relative',\n overflow: 'visible',\n }}\n >\n <svg\n width={width}\n height={height}\n style={{ position: 'absolute', top: 0, left: 0, overflow: 'hidden', borderRadius: '2px' }}\n >\n <path\n d={pathData}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <circle\n cx={4 + (width - 8)}\n cy={normalizedValues[normalizedValues.length - 1]}\n r={1.5}\n fill={color}\n />\n </svg>\n {forced && (\n <span style={forcedTagStyle} aria-label=\"Forced\">\n F\n </span>\n )}\n </div>\n );\n};\n\nconst hexToRgba = (hex: string, alpha: number) => {\n const r = parseInt(hex.slice(1, 3), 16);\n const g = parseInt(hex.slice(3, 5), 16);\n const b = parseInt(hex.slice(5, 7), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n};\n\nexport const CircularGauge = ({\n value,\n max,\n unit,\n label,\n size = 60,\n color = 'rgba(255, 255, 255, 0.9)',\n forced = false,\n realValue,\n}: CircularGaugeProps) => {\n const percentage = Math.min((value / max) * 100, 100);\n const strokeWidth = 4;\n const radius = (size - strokeWidth) / 2;\n const circumference = radius * 2 * Math.PI;\n const strokeDasharray = circumference;\n const strokeDashoffset = circumference - (percentage / 100) * circumference;\n\n const isHex = color.startsWith('#');\n const innerCircleColor = isHex ? hexToRgba(color, 0.8) : color;\n const borderColor = isHex ? hexToRgba(color, 0.9) : color;\n const backgroundColor = isHex ? hexToRgba(color, 0.15) : color;\n const borderStrokeColor = isHex ? hexToRgba(color, 0.4) : color;\n\n return (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n }}\n >\n <div\n style={{\n position: 'relative',\n width: size,\n height: size,\n borderRadius: '50%',\n backgroundColor,\n border: forced ? `2px solid ${FORCED_MARK_COLOR.border}` : `1px solid ${borderStrokeColor}`,\n }}\n >\n <svg width={size} height={size} style={{ transform: 'rotate(-90deg)' }}>\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius - strokeWidth - 2}\n fill={innerCircleColor}\n stroke={borderColor}\n strokeWidth={1}\n />\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n stroke=\"rgba(255, 255, 255, 0.2)\"\n strokeWidth={strokeWidth}\n fill=\"transparent\"\n />\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n stroke={color}\n strokeWidth={strokeWidth}\n fill=\"transparent\"\n strokeDasharray={strokeDasharray}\n strokeDashoffset={strokeDashoffset}\n strokeLinecap=\"round\"\n style={{ transition: 'stroke-dashoffset 0.5s ease-in-out' }}\n />\n </svg>\n <div\n style={{\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n fontSize: '11px',\n fontWeight: '700',\n color: 'white',\n textAlign: 'center',\n textShadow: '0 1px 2px rgba(0, 0, 0, 0.5)',\n }}\n >\n {value.toFixed(1)}\n <div style={{ fontSize: '8px', opacity: 0.9, color: 'white' }}>{unit}</div>\n </div>\n </div>\n <div\n style={{\n fontSize: '9px',\n marginTop: '4px',\n opacity: 0.9,\n color: 'white',\n }}\n >\n {label}\n </div>\n {forced && (\n <span\n aria-label=\"Forced\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '14px',\n height: '14px',\n fontSize: '9px',\n fontWeight: 700,\n color: FORCED_MARK_COLOR.text,\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n borderRadius: '50%',\n marginTop: '2px',\n }}\n >\n F\n </span>\n )}\n {forced && realValue !== undefined && (\n <div style={{ fontSize: '8px', opacity: 0.8, color: 'white', marginTop: '1px' }}>\n REAL {realValue.toFixed(1)} {unit}\n </div>\n )}\n </div>\n );\n};\n\nexport const Sparkline = ({ data, width, height, color, forced = false }: SparklineProps) => {\n if (!data || data.length < 2) {\n return null;\n }\n\n const max = Math.max(...data);\n const min = Math.min(...data);\n const range = max - min;\n\n if (range === 0) {\n return null;\n }\n\n const points = data\n .map((value, index) => {\n const x = (index / (data.length - 1)) * (width - 8);\n const y = height - 8 - ((value - min) / range) * (height - 8);\n return `${x + 4},${y + 4}`;\n })\n .join(' ');\n\n return (\n <div\n style={{\n position: 'relative',\n width,\n height,\n backgroundColor: 'rgba(0, 0, 0, 0.7)',\n border: forced ? `1px solid ${FORCED_MARK_COLOR.border}` : '1px solid rgba(255, 255, 255, 0.3)',\n borderRadius: '4px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n boxShadow: '0 1px 3px rgba(0, 0, 0, 0.2)',\n }}\n >\n <svg width={width} height={height}>\n <polyline points={points} fill=\"none\" stroke={color} strokeWidth=\"1.5\" opacity=\"0.9\" />\n </svg>\n {forced && (\n <span style={forcedTagStyle} aria-label=\"Forced\">\n F\n </span>\n )}\n </div>\n );\n};\n\n/** Small glyph badge for EquipmentIndicator variants (#3607, R19). A glyph,\n * not a color change: this indicator's whole signal is color plus a short\n * label today, and color alone fails a colorblind operator. */\nconst EquipmentForcedBadge = () => (\n <span\n aria-label=\"Forced\"\n style={{\n position: 'absolute',\n top: '-4px',\n right: '-4px',\n width: '11px',\n height: '11px',\n borderRadius: '50%',\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n color: FORCED_MARK_COLOR.text,\n fontSize: '7px',\n fontWeight: 700,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n lineHeight: 1,\n }}\n >\n F\n </span>\n);\n\nexport const EquipmentIndicatorWithSparkline = ({\n svgX,\n svgY,\n color,\n label,\n size = 30,\n sparklineData,\n sparklineOffset = { x: 40, y: 0 },\n forced = false,\n}: EquipmentIndicatorWithSparklineProps) => {\n const hasSparkline =\n sparklineData &&\n sparklineData.length > 1 &&\n Math.max(...sparklineData) !== Math.min(...sparklineData);\n\n return (\n <>\n <SVGLockedOverlay svgX={svgX} svgY={svgY} style={{ transform: 'translate(-50%, -50%)' }}>\n <div style={{ position: 'relative', width: size, height: size }}>\n <div\n style={{\n width: size,\n height: size,\n borderRadius: '50%',\n backgroundColor: color,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: 'white',\n fontSize: '7px',\n fontWeight: '700',\n textAlign: 'center',\n boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',\n border: forced\n ? `2px solid ${FORCED_MARK_COLOR.border}`\n : '2px solid rgba(255, 255, 255, 0.3)',\n }}\n >\n {label}\n </div>\n {forced && <EquipmentForcedBadge />}\n </div>\n </SVGLockedOverlay>\n\n {hasSparkline && (\n <SVGLockedOverlay svgX={svgX + sparklineOffset.x} svgY={svgY + sparklineOffset.y}>\n <Sparkline data={sparklineData!} width={60} height={20} color={color} forced={forced} />\n </SVGLockedOverlay>\n )}\n </>\n );\n};\n\nexport const EquipmentIndicator = ({\n svgX,\n svgY,\n color,\n label,\n size = 30,\n style,\n className,\n forced = false,\n}: EquipmentIndicatorProps) => {\n const overlayStyle =\n style !== undefined\n ? { transform: 'translate(-50%, -50%)', ...style }\n : { transform: 'translate(-50%, -50%)' };\n\n return (\n <SVGLockedOverlay svgX={svgX} svgY={svgY} style={overlayStyle} className={className}>\n <div style={{ position: 'relative', width: size, height: size }}>\n <div\n style={{\n width: size,\n height: size,\n borderRadius: '50%',\n backgroundColor: color,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: 'white',\n fontSize: '7px',\n fontWeight: '700',\n textAlign: 'center',\n boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',\n border: forced\n ? `2px solid ${FORCED_MARK_COLOR.border}`\n : '2px solid rgba(255, 255, 255, 0.3)',\n }}\n >\n {label}\n </div>\n {forced && <EquipmentForcedBadge />}\n </div>\n </SVGLockedOverlay>\n );\n};\n","import React, { createContext, useContext, useState, useEffect } from 'react';\n\ntype Theme = 'modern' | 'hmi';\n\ninterface ThemeContextType {\n theme: Theme;\n toggleTheme: () => void;\n setTheme: (theme: Theme) => void;\n}\n\nconst ThemeContext = createContext<ThemeContextType | undefined>(undefined);\n\nexport const useTheme = () => {\n const context = useContext(ThemeContext);\n if (!context) {\n throw new Error('useTheme must be used within a ThemeProvider');\n }\n return context;\n};\n\ninterface ThemeProviderProps {\n children: React.ReactNode;\n}\n\nexport const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {\n const [theme, setThemeState] = useState<Theme>('modern');\n\n useEffect(() => {\n // Apply theme classes to document body\n document.body.className = '';\n document.body.classList.add(`theme-${theme}`);\n\n // Also apply to html element for global styling\n document.documentElement.className = '';\n document.documentElement.classList.add(`theme-${theme}`);\n }, [theme]);\n\n const toggleTheme = () => {\n setTheme(theme === 'modern' ? 'hmi' : 'modern');\n };\n\n const setTheme = (newTheme: Theme) => {\n setThemeState(newTheme);\n };\n\n return (\n <ThemeContext.Provider value={{ theme, toggleTheme, setTheme }}>\n {children}\n </ThemeContext.Provider>\n );\n};\n","import React from 'react';\nimport { useTheme } from './ThemeContext';\n\nexport interface ThemeToggleProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Size of the toggle */\n size?: 'small' | 'medium' | 'large';\n /** Position of the toggle */\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'inline';\n}\n\nexport const ThemeToggle: React.FC<ThemeToggleProps> = ({\n size = 'medium',\n position = 'top-right',\n className = '',\n ...props\n}) => {\n const { theme, toggleTheme } = useTheme();\n\n const sizeStyles = {\n small: { padding: '0.5rem 1rem', fontSize: '0.75rem' },\n medium: { padding: '0.75rem 1.5rem', fontSize: '0.875rem' },\n large: { padding: '1rem 2rem', fontSize: '1rem' },\n };\n\n const positionStyles = {\n 'top-left': { position: 'fixed' as const, top: '1rem', left: '1rem', zIndex: 1000 },\n 'top-right': { position: 'fixed' as const, top: '1rem', right: '1rem', zIndex: 1000 },\n 'bottom-left': { position: 'fixed' as const, bottom: '1rem', left: '1rem', zIndex: 1000 },\n 'bottom-right': { position: 'fixed' as const, bottom: '1rem', right: '1rem', zIndex: 1000 },\n inline: { position: 'relative' as const },\n };\n\n const baseStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '0.75rem',\n cursor: 'pointer',\n borderRadius: '0.5rem',\n border: '2px solid',\n fontFamily:\n 'ui-monospace, SFMono-Regular, \"SF Mono\", Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n transition: 'all 0.2s ease',\n userSelect: 'none',\n ...sizeStyles[size],\n ...positionStyles[position],\n };\n\n const modernStyle: React.CSSProperties = {\n ...baseStyle,\n background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 50%, #1d4ed8 100%)',\n borderColor: 'rgba(59, 130, 246, 0.4)',\n color: '#ffffff',\n boxShadow: '0 8px 32px rgba(59, 130, 246, 0.3)',\n };\n\n const hmiStyle: React.CSSProperties = {\n ...baseStyle,\n backgroundColor: '#666666',\n borderColor: '#999999',\n color: '#ffffff',\n boxShadow: 'none',\n };\n\n const currentStyle = theme === 'modern' ? modernStyle : hmiStyle;\n\n const toggleStyle: React.CSSProperties = {\n position: 'relative',\n width: '3rem',\n height: '1.5rem',\n borderRadius: '0.75rem',\n transition: 'all 0.2s ease',\n backgroundColor: theme === 'modern' ? '#3b82f6' : '#808080',\n border: `2px solid ${theme === 'modern' ? '#2563eb' : '#666666'}`,\n };\n\n const thumbStyle: React.CSSProperties = {\n position: 'absolute',\n top: '0.125rem',\n left: theme === 'modern' ? '0.125rem' : '1.375rem',\n width: '1rem',\n height: '1rem',\n borderRadius: '50%',\n backgroundColor: '#ffffff',\n transition: 'all 0.2s ease',\n boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)',\n };\n\n return (\n <div style={currentStyle} onClick={toggleTheme} className={className} {...props}>\n <span>{theme === 'modern' ? '🎨 Modern' : '🏭 HMI'}</span>\n <div style={toggleStyle}>\n <div style={thumbStyle} />\n </div>\n <span style={{ fontSize: '0.75rem', opacity: 0.8 }}>\n {theme === 'modern' ? 'Industrial Design' : 'High Performance'}\n </span>\n </div>\n );\n};\n","import React, { useCallback, useState, useEffect, useRef } from 'react';\nimport type { Diagram } from '../../diagram/schema';\nimport type { TelemetryMap } from '../../diagram/schema';\nimport { NodeRenderer } from './NodeRenderer';\nimport { PipeRenderer } from './PipeRenderer';\nimport { DataOverlay } from './DataOverlay';\nimport { PositionPanel } from './PositionPanel';\nimport { useViewBox, useSelection, useDrag, useKeyboardShortcuts } from '../../diagram/hooks';\nimport type { SelectionState } from '../../diagram/hooks';\nimport { getSymbolDefinition } from '../../diagram/symbols';\nimport { generateRoutePoints, getPortWorldPosition } from '../../diagram/utils/portCalculations';\nimport type { StatusColorMap } from '../../theme/statusColors';\n\nexport interface PIDCanvasFeatures {\n /** Enable pan interaction (default: true) */\n pan?: boolean;\n /** Enable zoom interaction (default: true) */\n zoom?: boolean;\n /** Enable selection interaction (default: false) */\n selection?: boolean;\n /** Allow multi-selection with Ctrl/Cmd key (default: true) */\n multiSelect?: boolean;\n /** Enable telemetry data overlays (default: false) */\n telemetry?: boolean;\n /** Scale factor for telemetry displays (default: 1.0, range: 0.5-2.0) */\n telemetryScale?: number;\n /** Enable drag-and-drop for nodes (default: false) */\n dragNodes?: boolean;\n /** Snap dragged nodes to grid (0 = no snap) */\n snapToGrid?: number;\n /** Enable keyboard shortcuts for delete/copy/paste (default: false) */\n keyboardShortcuts?: boolean;\n /** Enable pipe waypoint editing (drag waypoints, add/remove) (default: false) */\n editPipeRoutes?: boolean;\n /** Enable dropping symbols from library to add nodes (default: false) */\n allowSymbolDrop?: boolean;\n /** Enable connection drawing mode to create pipes between nodes (default: false) */\n connectionMode?: boolean;\n /** Show background grid (default: true) */\n showGrid?: boolean;\n /** Background color for canvas (default: '#ffffff') */\n backgroundColor?: string;\n}\n\nexport interface PIDCanvasProps extends React.HTMLAttributes<HTMLDivElement> {\n /** The diagram to render */\n diagram: Diagram;\n /** Optional className for the container */\n className?: string;\n /** Optional inline styles for the container */\n style?: React.CSSProperties;\n /** Enable interactive features (pan, zoom, selection, telemetry, dragNodes) */\n features?: PIDCanvasFeatures;\n /** Callback when selection changes */\n onSelectionChange?: (selection: SelectionState) => void;\n /** Callback when a node is clicked */\n onNodeClick?: (nodeId: string) => void;\n /** Callback when a pipe is clicked */\n onPipeClick?: (pipeId: string) => void;\n /** Callback when a node is moved (drag-and-drop) */\n onNodeMove?: (nodeId: string, newX: number, newY: number) => void;\n /** Callback when diagram changes (for internal state updates) */\n onDiagramChange?: (diagram: Diagram) => void;\n /** Callback when items are deleted */\n onDelete?: (nodeIds: string[], pipeIds: string[]) => void;\n /** Callback when items are copied (returns copied data for external storage) */\n onItemsCopied?: (nodeIds: string[], pipeIds: string[]) => void;\n /** Callback when paste is requested (external code should handle the paste logic) */\n onPaste?: () => void;\n /** Callback when a symbol is dropped onto canvas (returns symbol ID and position) */\n onSymbolDrop?: (symbolId: string, position: { x: number; y: number }) => void;\n /** Callback when a pipe is created via connection mode */\n onPipeCreated?: (\n sourceNodeId: string,\n targetNodeId: string,\n sourcePortId?: string,\n targetPortId?: string\n ) => void;\n /** Callback when canvas is mounted, provides control functions */\n onMounted?: (controls: PIDCanvasControls) => void;\n /** Telemetry data bindings for live values */\n telemetry?: TelemetryMap;\n /** Custom status-to-color map for node rendering (overrides ISA-101 defaults) */\n statusColorMap?: StatusColorMap;\n}\n\nexport interface PIDCanvasControls {\n /** Fit view to show all diagram content */\n fitToContent: (padding?: number) => void;\n /** Set zoom level (1 = 100%, 2 = 200%, etc.) */\n setZoom: (zoomLevel: number) => void;\n /** Get current zoom level */\n getZoom: () => number;\n}\n\n/**\n * Default feature set. Hoisted to module scope so an omitted `features` prop\n * does not mint a fresh object per render - that would defeat the React.memo\n * boundary on PIDCanvas below (renderer-OOM fix).\n */\nconst DEFAULT_FEATURES: PIDCanvasFeatures = {\n pan: true,\n zoom: true,\n selection: false,\n multiSelect: true,\n telemetry: false,\n dragNodes: false,\n};\n\n/**\n * PIDCanvas - Interactive canvas for rendering P&ID diagrams\n *\n * Features:\n * - Renders nodes (PR #3) and pipes (PR #4) with static layout\n * - Drag-and-drop: Drag nodes to reposition (PR #8)\n *\n * Memoized (renderer-OOM fix): a host whose screen re-renders for reasons\n * unrelated to the canvas (panel state, connection status, ...) should not pay\n * a full-canvas reconciliation. The memo only pays off when the host passes\n * identity-stable props (memoized features/telemetry/style, useCallback\n * handlers); an unstable host degrades gracefully to the old behavior.\n *\n * Future: Editing tools, symbol library UI\n */\nfunction PIDCanvasImpl({\n diagram,\n className = '',\n style,\n features = DEFAULT_FEATURES,\n onSelectionChange,\n onNodeClick,\n onPipeClick,\n onNodeMove,\n onDiagramChange,\n onDelete,\n onItemsCopied,\n onPaste,\n onSymbolDrop,\n onPipeCreated,\n onMounted,\n telemetry,\n statusColorMap,\n ...props\n}: PIDCanvasProps): React.ReactElement {\n const [localDiagram, setLocalDiagram] = useState(diagram);\n const [dragOverPosition, setDragOverPosition] = useState<{ x: number; y: number } | null>(null);\n\n // Connection mode state\n const [isConnecting, setIsConnecting] = useState(false);\n const [connectionSource, setConnectionSource] = useState<string | null>(null);\n const [connectionSourcePort, setConnectionSourcePort] = useState<string | null>(null);\n const [connectionCursor, setConnectionCursor] = useState<{ x: number; y: number } | null>(null);\n const [hoveredNode, setHoveredNode] = useState<string | null>(null);\n const [hoveredPort, setHoveredPort] = useState<string | null>(null);\n\n // Waypoint selection state (separate from node/pipe selection)\n const [selectedWaypoint, setSelectedWaypoint] = useState<{\n pipeId: string;\n pointIndex: number;\n } | null>(null);\n\n // Undo/Redo state - using refs to avoid re-renders\n const historyStack = useRef<Diagram[]>([]);\n const redoStack = useRef<Diagram[]>([]);\n const isUndoRedoAction = useRef(false);\n const isInternalChange = useRef(false);\n const lastInternalDiagram = useRef<Diagram | null>(null);\n\n // Sync external diagram changes to local state\n useEffect(() => {\n // Only reset history if this is a truly external change (not from our callbacks)\n const isExternal = !isInternalChange.current && diagram !== lastInternalDiagram.current;\n\n // Only update state if this is an external change\n // This is a legitimate use of setState in effect - syncing external prop to local state\n if (isExternal) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setLocalDiagram(diagram);\n\n // Reset history for external changes\n if (!isUndoRedoAction.current) {\n historyStack.current = [];\n redoStack.current = [];\n }\n }\n\n // Reset the internal change flag\n isInternalChange.current = false;\n }, [diagram]);\n\n const {\n viewBox: controlledViewBox,\n svgRef,\n screenToWorld,\n fitToContent: fitToContentViewBox,\n setViewBox,\n } = useViewBox({\n initialViewBox: diagram.viewBox,\n enablePan: features.pan ?? true,\n enableZoom: features.zoom ?? true,\n });\n\n // Calculate content bounds\n const calculateContentBounds = useCallback(() => {\n const nodes = localDiagram.nodes.filter((n) => n.visible !== false);\n if (nodes.length === 0) {\n return null;\n }\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n nodes.forEach((node) => {\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol) return;\n\n const nodeMinX = node.transform.x;\n const nodeMinY = node.transform.y;\n const nodeMaxX = node.transform.x + symbol.viewBox.width;\n const nodeMaxY = node.transform.y + symbol.viewBox.height;\n\n minX = Math.min(minX, nodeMinX);\n minY = Math.min(minY, nodeMinY);\n maxX = Math.max(maxX, nodeMaxX);\n maxY = Math.max(maxY, nodeMaxY);\n });\n\n return { minX, minY, maxX, maxY };\n }, [localDiagram.nodes]);\n\n // Expose controls via onMounted callback\n useEffect(() => {\n if (onMounted) {\n const controls: PIDCanvasControls = {\n fitToContent: (padding = 50) => {\n const bounds = calculateContentBounds();\n if (bounds) {\n fitToContentViewBox(bounds, padding);\n }\n },\n setZoom: (zoomLevel: number) => {\n const centerX = controlledViewBox.x + controlledViewBox.width / 2;\n const centerY = controlledViewBox.y + controlledViewBox.height / 2;\n const newWidth = diagram.viewBox.width / zoomLevel;\n const newHeight = diagram.viewBox.height / zoomLevel;\n setViewBox({\n x: centerX - newWidth / 2,\n y: centerY - newHeight / 2,\n width: newWidth,\n height: newHeight,\n });\n },\n getZoom: () => {\n return diagram.viewBox.width / controlledViewBox.width;\n },\n };\n onMounted(controls);\n }\n }, [\n onMounted,\n calculateContentBounds,\n fitToContentViewBox,\n setViewBox,\n controlledViewBox,\n diagram.viewBox,\n ]);\n\n const { selection, selectNode, selectPipe, clearSelection } = useSelection({\n multiSelect: features.multiSelect ?? true,\n onSelectionChange,\n });\n\n const dragEnabled = features.dragNodes ?? false;\n const selectionEnabled = features.selection ?? false;\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Push current state to history (for undo)\n const pushToHistory = useCallback((currentDiagram: Diagram) => {\n if (isUndoRedoAction.current) return; // Don't add to history during undo/redo\n\n // Deep clone to avoid reference issues\n const snapshot = JSON.parse(JSON.stringify(currentDiagram));\n historyStack.current.push(snapshot);\n\n // Limit history size to 15 states\n if (historyStack.current.length > 15) {\n historyStack.current.shift();\n }\n\n // Clear redo stack on new action\n redoStack.current = [];\n }, []);\n\n // Undo function\n const undo = useCallback(() => {\n if (historyStack.current.length === 0) return;\n\n isUndoRedoAction.current = true;\n\n // Save current state to redo stack\n const currentSnapshot = JSON.parse(JSON.stringify(localDiagram));\n redoStack.current.push(currentSnapshot);\n\n // Pop from history and restore\n const previousState = historyStack.current.pop()!;\n isInternalChange.current = true;\n lastInternalDiagram.current = previousState;\n setLocalDiagram(previousState);\n onDiagramChange?.(previousState);\n\n // Reset flag after state updates\n setTimeout(() => {\n isUndoRedoAction.current = false;\n }, 0);\n }, [localDiagram, onDiagramChange]);\n\n // Redo function\n const redo = useCallback(() => {\n if (redoStack.current.length === 0) return;\n\n isUndoRedoAction.current = true;\n\n // Save current state to history\n const currentSnapshot = JSON.parse(JSON.stringify(localDiagram));\n historyStack.current.push(currentSnapshot);\n\n // Pop from redo stack and restore\n const nextState = redoStack.current.pop()!;\n isInternalChange.current = true;\n lastInternalDiagram.current = nextState;\n setLocalDiagram(nextState);\n onDiagramChange?.(nextState);\n\n // Reset flag after state updates\n setTimeout(() => {\n isUndoRedoAction.current = false;\n }, 0);\n }, [localDiagram, onDiagramChange]);\n\n // Keyboard shortcuts for undo/redo\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // Check for Ctrl+Z (Windows/Linux) or Cmd+Z (Mac)\n const isCtrlOrCmd = e.ctrlKey || e.metaKey;\n\n if (isCtrlOrCmd && e.key === 'z' && !e.shiftKey) {\n e.preventDefault();\n undo();\n } else if (isCtrlOrCmd && e.key === 'z' && e.shiftKey) {\n e.preventDefault();\n redo();\n } else if (isCtrlOrCmd && e.key === 'y') {\n // Ctrl+Y is also a common redo shortcut\n e.preventDefault();\n redo();\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [undo, redo]);\n\n // Keyboard nudging for selected nodes and waypoints\n useEffect(() => {\n if (!features.dragNodes) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n // Only handle arrow keys when something is selected\n if (selection.selectedNodeIds.size === 0 && !selectedWaypoint) return;\n\n // Ignore if user is typing in an input\n if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {\n return;\n }\n\n const nudgeAmount = e.shiftKey ? 10 : 1; // Shift for larger nudge\n let dx = 0;\n let dy = 0;\n\n switch (e.key) {\n case 'ArrowLeft':\n dx = -nudgeAmount;\n e.preventDefault();\n break;\n case 'ArrowRight':\n dx = nudgeAmount;\n e.preventDefault();\n break;\n case 'ArrowUp':\n dy = -nudgeAmount;\n e.preventDefault();\n break;\n case 'ArrowDown':\n dy = nudgeAmount;\n e.preventDefault();\n break;\n default:\n return;\n }\n\n // Update selected waypoint if one is selected\n if (selectedWaypoint) {\n pushToHistory(localDiagram);\n\n const updatedPipes = localDiagram.pipes.map((pipe) => {\n if (pipe.id === selectedWaypoint.pipeId) {\n const updatedRoutePoints = pipe.routePoints.map((point, idx) =>\n idx === selectedWaypoint.pointIndex ? { x: point.x + dx, y: point.y + dy } : point\n );\n return { ...pipe, routePoints: updatedRoutePoints };\n }\n return pipe;\n });\n\n const updatedDiagram = { ...localDiagram, pipes: updatedPipes };\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n return;\n }\n\n // Update all selected nodes\n if (selectionEnabled && selection.selectedNodeIds.size > 0) {\n pushToHistory(localDiagram);\n\n const updatedNodes = localDiagram.nodes.map((node) =>\n selection.selectedNodeIds.has(node.id)\n ? {\n ...node,\n transform: {\n ...node.transform,\n x: node.transform.x + dx,\n y: node.transform.y + dy,\n },\n }\n : node\n );\n\n const updatedDiagram = { ...localDiagram, nodes: updatedNodes };\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n\n // Call onNodeMove for each moved node\n selection.selectedNodeIds.forEach((nodeId) => {\n const node = updatedNodes.find((n) => n.id === nodeId);\n if (node) {\n onNodeMove?.(nodeId, node.transform.x, node.transform.y);\n }\n });\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [\n selectionEnabled,\n features.dragNodes,\n selection.selectedNodeIds,\n selectedWaypoint,\n localDiagram,\n onDiagramChange,\n onNodeMove,\n pushToHistory,\n ]);\n\n // Handler for node position panel changes\n const handlePositionChange = useCallback(\n (nodeId: string, x: number, y: number) => {\n pushToHistory(localDiagram);\n\n const updatedNodes = localDiagram.nodes.map((node) =>\n node.id === nodeId\n ? {\n ...node,\n transform: {\n ...node.transform,\n x,\n y,\n },\n }\n : node\n );\n\n const updatedDiagram = { ...localDiagram, nodes: updatedNodes };\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n onNodeMove?.(nodeId, x, y);\n },\n [localDiagram, onDiagramChange, onNodeMove, pushToHistory]\n );\n\n // Handler for waypoint position panel changes\n const handleWaypointPositionChange = useCallback(\n (pipeId: string, pointIndex: number, x: number, y: number) => {\n pushToHistory(localDiagram);\n\n const updatedPipes = localDiagram.pipes.map((pipe) => {\n if (pipe.id === pipeId) {\n const updatedRoutePoints = pipe.routePoints.map((point, idx) =>\n idx === pointIndex ? { x, y } : point\n );\n return { ...pipe, routePoints: updatedRoutePoints };\n }\n return pipe;\n });\n\n const updatedDiagram = { ...localDiagram, pipes: updatedPipes };\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n },\n [localDiagram, onDiagramChange, pushToHistory]\n );\n\n const handleDragEnd = useCallback(\n (nodeId: string, offset: { x: number; y: number }, finalPosition: { x: number; y: number }) => {\n // If drag offset is zero or very small, treat as a click for selection\n const dragDistance = Math.sqrt(offset.x ** 2 + offset.y ** 2);\n if (dragDistance < 2) {\n // This was a click, not a drag - handle selection\n if (selectionEnabled) {\n // We need to check if this was a Ctrl/Cmd click, but we don't have the event here\n // For now, just select without modifier\n selectNode(nodeId, false);\n }\n return;\n }\n\n pushToHistory(localDiagram);\n\n // Update node position in diagram\n const updatedNodes = localDiagram.nodes.map((node) =>\n node.id === nodeId\n ? {\n ...node,\n transform: {\n ...node.transform,\n x: finalPosition.x,\n y: finalPosition.y,\n },\n }\n : node\n );\n\n // Find affected pipes (pipes connected to this node)\n const affectedPipes = localDiagram.pipes.filter(\n (pipe) => pipe.fromNodeId === nodeId || pipe.toNodeId === nodeId\n );\n\n // Recalculate routes for affected pipes\n const updatedPipes = localDiagram.pipes.map((pipe) => {\n if (!affectedPipes.find((p) => p.id === pipe.id)) return pipe;\n\n const sourceNode = updatedNodes.find((n) => n.id === pipe.fromNodeId);\n const targetNode = updatedNodes.find((n) => n.id === pipe.toNodeId);\n\n if (!sourceNode || !targetNode) return pipe;\n\n // If pipe has custom waypoints (more than 2 points), preserve ALL waypoints\n // including endpoints - user has manually positioned them\n if (pipe.routePoints.length > 2) {\n return pipe; // Keep all waypoints as-is, including endpoints\n }\n\n // For simple pipes (2 points), recalculate to follow node ports\n const newRoutePoints = generateRoutePoints(\n sourceNode,\n pipe.fromPortId ?? 'output',\n targetNode,\n pipe.toPortId ?? 'input'\n );\n\n return {\n ...pipe,\n routePoints: newRoutePoints,\n };\n });\n\n const updatedDiagram: Diagram = {\n ...localDiagram,\n nodes: updatedNodes,\n pipes: updatedPipes,\n };\n\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n onNodeMove?.(nodeId, finalPosition.x, finalPosition.y);\n },\n [localDiagram, onDiagramChange, onNodeMove, selectionEnabled, selectNode, pushToHistory]\n );\n\n const { dragState, startDrag } = useDrag({\n snapToGrid: features.snapToGrid ?? 0,\n screenToWorld,\n onDragEnd: handleDragEnd,\n });\n\n // Separate drag state for waypoints\n const { dragState: waypointDragState, startDrag: startWaypointDrag } = useDrag({\n snapToGrid: features.snapToGrid ?? 0,\n screenToWorld,\n onDragEnd: (\n pipeId: string,\n _offset: { x: number; y: number },\n finalPosition: { x: number; y: number }\n ) => {\n pushToHistory(localDiagram);\n\n // Update waypoint position\n const [actualPipeId, pointIndex] = pipeId.split(':');\n const idx = parseInt(pointIndex, 10);\n\n const updatedPipes = localDiagram.pipes.map((pipe) => {\n if (pipe.id !== actualPipeId) return pipe;\n\n const updatedRoutePoints = pipe.routePoints.map((point, i) =>\n i === idx ? { x: finalPosition.x, y: finalPosition.y } : point\n );\n\n return { ...pipe, routePoints: updatedRoutePoints };\n });\n\n const updatedDiagram = { ...localDiagram, pipes: updatedPipes };\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n },\n });\n\n const handleNodeDragStart = useCallback(\n (nodeId: string, event: React.MouseEvent | React.TouchEvent) => {\n const node = localDiagram.nodes.find((n) => n.id === nodeId);\n if (!node) return;\n startDrag(nodeId, event, { x: node.transform.x, y: node.transform.y });\n },\n [localDiagram.nodes, startDrag]\n );\n\n const handleWaypointClick = useCallback(\n (pipeId: string, pointIndex: number) => {\n // Select this waypoint\n setSelectedWaypoint({ pipeId, pointIndex });\n\n // Clear node/pipe selection\n if (selectionEnabled) {\n clearSelection();\n }\n },\n [selectionEnabled, clearSelection]\n );\n\n const handleWaypointDragStart = useCallback(\n (pipeId: string, pointIndex: number, event: React.MouseEvent | React.TouchEvent) => {\n const pipe = localDiagram.pipes.find((p) => p.id === pipeId);\n if (!pipe || pointIndex >= pipe.routePoints.length) return;\n\n const point = pipe.routePoints[pointIndex];\n\n // Use compound ID to track both pipe and point\n startWaypointDrag(`${pipeId}:${pointIndex}`, event, { x: point.x, y: point.y });\n },\n [localDiagram.pipes, startWaypointDrag]\n );\n\n const handlePipeSegmentClick = useCallback(\n (pipeId: string, position: { x: number; y: number }, segmentIndex: number) => {\n pushToHistory(localDiagram);\n\n const updatedPipes = localDiagram.pipes.map((pipe) => {\n if (pipe.id !== pipeId) return pipe;\n\n // Insert new waypoint after segmentIndex\n const updatedRoutePoints = [\n ...pipe.routePoints.slice(0, segmentIndex + 1),\n position,\n ...pipe.routePoints.slice(segmentIndex + 1),\n ];\n\n return { ...pipe, routePoints: updatedRoutePoints };\n });\n\n const updatedDiagram = { ...localDiagram, pipes: updatedPipes };\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n },\n [localDiagram, onDiagramChange, pushToHistory]\n );\n\n const telemetryEnabled = features.telemetry ?? false;\n\n const handleDelete = useCallback(() => {\n if (!selectionEnabled) return;\n const { selectedNodeIds, selectedPipeIds } = selection;\n\n if (selectedNodeIds.size === 0 && selectedPipeIds.size === 0) return;\n\n pushToHistory(localDiagram);\n\n // Remove selected nodes\n const updatedNodes = localDiagram.nodes.filter((node) => !selectedNodeIds.has(node.id));\n\n // Remove selected pipes and pipes connected to deleted nodes\n const updatedPipes = localDiagram.pipes.filter(\n (pipe) =>\n !selectedPipeIds.has(pipe.id) &&\n !selectedNodeIds.has(pipe.fromNodeId) &&\n !selectedNodeIds.has(pipe.toNodeId)\n );\n\n const updatedDiagram = {\n ...localDiagram,\n nodes: updatedNodes,\n pipes: updatedPipes,\n };\n\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n clearSelection();\n onDiagramChange?.(updatedDiagram);\n onDelete?.(Array.from(selectedNodeIds), Array.from(selectedPipeIds));\n }, [\n selection,\n localDiagram,\n selectionEnabled,\n clearSelection,\n onDiagramChange,\n onDelete,\n pushToHistory,\n ]);\n\n const handleCopy = useCallback(() => {\n if (!selectionEnabled) return;\n const { selectedNodeIds, selectedPipeIds } = selection;\n\n if (selectedNodeIds.size === 0 && selectedPipeIds.size === 0) return;\n\n onItemsCopied?.(Array.from(selectedNodeIds), Array.from(selectedPipeIds));\n }, [selection, selectionEnabled, onItemsCopied]);\n\n const handlePaste = useCallback(() => {\n if (!selectionEnabled) return;\n onPaste?.();\n }, [selectionEnabled, onPaste]);\n\n // useCallback (here and on the node/pipe click handlers below) keeps the\n // identity handed to the memoized NodeRenderer/PipeRenderer stable across\n // telemetry-driven re-renders, so those layers can bail out (renderer-OOM\n // fix). Their deps change on interaction (selection, connection mode), never\n // on a telemetry tick.\n const handleNodeDoubleClick = useCallback(\n (nodeId: string, event: React.MouseEvent) => {\n if (!selectionEnabled) return;\n console.log('Node double-clicked:', nodeId);\n selectNode(nodeId, event.ctrlKey || event.metaKey);\n },\n [selectionEnabled, selectNode]\n );\n\n useKeyboardShortcuts({\n enabled: features.keyboardShortcuts ?? false,\n onDelete: handleDelete,\n onCopy: handleCopy,\n onPaste: handlePaste,\n });\n\n const handleNodeClick = useCallback(\n (nodeId: string, event: React.MouseEvent) => {\n // Connection mode takes priority\n const connectionModeEnabled = features.connectionMode ?? false;\n\n if (connectionModeEnabled && isConnecting) {\n // Second click - complete connection\n if (connectionSource && connectionSource !== nodeId) {\n // Use hovered port if available, otherwise let handler choose\n onPipeCreated?.(\n connectionSource,\n nodeId,\n connectionSourcePort || undefined,\n hoveredPort || undefined\n );\n setIsConnecting(false);\n setConnectionSource(null);\n setConnectionSourcePort(null);\n setConnectionCursor(null);\n setHoveredPort(null);\n }\n return;\n }\n\n if (connectionModeEnabled && !isConnecting) {\n // First click - start connection\n setIsConnecting(true);\n setConnectionSource(nodeId);\n setConnectionSourcePort(hoveredPort);\n return;\n }\n\n // Normal selection mode\n if (selectionEnabled) {\n selectNode(nodeId, event.ctrlKey || event.metaKey);\n }\n onNodeClick?.(nodeId);\n },\n [\n features.connectionMode,\n isConnecting,\n connectionSource,\n connectionSourcePort,\n hoveredPort,\n onPipeCreated,\n selectionEnabled,\n selectNode,\n onNodeClick,\n ]\n );\n\n const handlePipeClick = useCallback(\n (pipeId: string, event: React.MouseEvent) => {\n if (selectionEnabled) {\n selectPipe(pipeId, event.ctrlKey || event.metaKey);\n }\n onPipeClick?.(pipeId);\n },\n [selectionEnabled, selectPipe, onPipeClick]\n );\n\n const handleCanvasClick = (e: React.MouseEvent<SVGSVGElement>) => {\n // Don't clear selection if clicking on a waypoint or its parent elements\n const target = e.target as Element;\n\n // Check if target or any parent has the pipe-route-point class\n if (target.closest('.pipe-route-point')) {\n return;\n }\n\n if (selectionEnabled) {\n clearSelection();\n }\n setSelectedWaypoint(null);\n };\n\n const handleDragOver = useCallback(\n (event: React.DragEvent) => {\n if (!features.allowSymbolDrop) return;\n\n event.preventDefault();\n event.dataTransfer.dropEffect = 'copy';\n\n // Convert screen coordinates to world coordinates\n if (!svgRef.current) return;\n\n const worldPos = screenToWorld(event.clientX, event.clientY);\n\n setDragOverPosition(worldPos);\n },\n [features.allowSymbolDrop, screenToWorld, svgRef]\n );\n\n const handleDrop = useCallback(\n (event: React.DragEvent) => {\n if (!features.allowSymbolDrop) return;\n\n event.preventDefault();\n setDragOverPosition(null);\n\n // Get symbol ID from drag data\n const symbolId = event.dataTransfer.getData('application/symbol-id');\n if (!symbolId) return;\n\n // Convert drop position to world coordinates\n if (!svgRef.current) return;\n const worldPos = screenToWorld(event.clientX, event.clientY);\n\n onSymbolDrop?.(symbolId, worldPos);\n },\n [features.allowSymbolDrop, screenToWorld, onSymbolDrop, svgRef]\n );\n\n const handleDragLeave = useCallback(() => {\n setDragOverPosition(null);\n }, []);\n\n const handleMouseMove = useCallback(\n (event: React.MouseEvent<SVGSVGElement>) => {\n const connectionModeEnabled = features.connectionMode ?? false;\n\n if (connectionModeEnabled) {\n const worldPos = screenToWorld(event.clientX, event.clientY);\n\n // Update cursor position if connecting\n if (isConnecting && connectionSource) {\n setConnectionCursor(worldPos);\n }\n\n // Always track hovered port in connection mode (for both source and target selection)\n let foundPort: string | null = null;\n let foundNode: string | null = null;\n\n for (const node of localDiagram.nodes) {\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol || !symbol.ports) continue;\n\n for (const port of symbol.ports) {\n const portPos = getPortWorldPosition(node, port.id);\n if (!portPos) continue;\n\n const distance = Math.sqrt(\n (worldPos.x - portPos.x) ** 2 + (worldPos.y - portPos.y) ** 2\n );\n\n // Increased detection radius to 15 for better clickability (visual port is radius 5-8)\n if (distance < 15) {\n foundPort = port.id;\n foundNode = node.id;\n break;\n }\n }\n if (foundPort) break;\n }\n\n setHoveredPort(foundPort);\n setHoveredNode(foundNode);\n }\n },\n [isConnecting, connectionSource, screenToWorld, localDiagram.nodes, features.connectionMode]\n );\n\n const { name } = diagram;\n const viewBox = controlledViewBox;\n const displayDiagram = localDiagram;\n\n // Get first selected node for position panel\n const selectedNode =\n selectionEnabled && selection.selectedNodeIds.size === 1\n ? localDiagram.nodes.find((n) => selection.selectedNodeIds.has(n.id)) || null\n : null;\n\n return (\n <div\n ref={containerRef}\n className={`pid-canvas ${className}`.trim()}\n style={{\n width: '100%',\n height: '100%',\n position: 'relative',\n overflow: 'hidden',\n backgroundColor: '#f5f5f5',\n ...style,\n }}\n {...props}\n >\n <svg\n ref={svgRef}\n width=\"100%\"\n height=\"100%\"\n viewBox={`${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n style={{\n display: 'block',\n }}\n aria-label={`P&ID diagram: ${name}`}\n role=\"img\"\n onClick={handleCanvasClick}\n onMouseMove={handleMouseMove}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n onDragLeave={handleDragLeave}\n >\n {/* Background - solid color or grid */}\n {features.showGrid !== false ? (\n <>\n <defs>\n {/* Minor grid - 5 unit increments */}\n <pattern id=\"grid-minor\" width=\"5\" height=\"5\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M 5 0 L 0 0 0 5\" fill=\"none\" stroke=\"#e5e7eb\" strokeWidth=\"0.5\" />\n </pattern>\n {/* Major grid - 25 unit increments */}\n <pattern id=\"grid-major\" width=\"25\" height=\"25\" patternUnits=\"userSpaceOnUse\">\n <rect width=\"25\" height=\"25\" fill=\"url(#grid-minor)\" />\n <path d=\"M 25 0 L 0 0 0 25\" fill=\"none\" stroke=\"#d1d5db\" strokeWidth=\"1\" />\n </pattern>\n </defs>\n <rect\n x={Math.floor(viewBox.x / 25) * 25 - viewBox.width}\n y={Math.floor(viewBox.y / 25) * 25 - viewBox.height}\n width={Math.ceil((viewBox.width * 3) / 25) * 25}\n height={Math.ceil((viewBox.height * 3) / 25) * 25}\n fill=\"url(#grid-major)\"\n />\n </>\n ) : (\n <rect\n x={Math.floor(viewBox.x / 25) * 25 - viewBox.width}\n y={Math.floor(viewBox.y / 25) * 25 - viewBox.height}\n width={Math.ceil((viewBox.width * 3) / 25) * 25}\n height={Math.ceil((viewBox.height * 3) / 25) * 25}\n fill={features.backgroundColor ?? '#ffffff'}\n />\n )}\n\n {/* Render pipes (behind nodes for proper layering) */}\n <PipeRenderer\n pipes={displayDiagram.pipes}\n selectedPipeIds={selectionEnabled ? selection.selectedPipeIds : undefined}\n onPipeClick={selectionEnabled ? handlePipeClick : undefined}\n enableWaypointEditing={false}\n onWaypointDragStart={undefined}\n editingPipeId={null}\n draggingWaypointIndex={null}\n waypointDragOffset={undefined}\n onPipeSegmentClick={undefined}\n />\n\n {/* Render nodes */}\n <NodeRenderer\n nodes={displayDiagram.nodes}\n selectedNodeIds={selectionEnabled ? selection.selectedNodeIds : undefined}\n onNodeClick={\n onNodeClick\n ? handleNodeClick\n : selectionEnabled || features.connectionMode\n ? handleNodeClick\n : undefined\n }\n onNodeDoubleClick={selectionEnabled ? handleNodeDoubleClick : undefined}\n enableDrag={dragEnabled && !isConnecting}\n onNodeDragStart={dragEnabled && !isConnecting ? handleNodeDragStart : undefined}\n draggingNodeId={dragState.draggedId}\n dragOffset={dragState.offset}\n statusColorMap={statusColorMap}\n />\n\n {/* Render waypoint editing controls on top of nodes */}\n {(features.editPipeRoutes ?? false) && (\n <PipeRenderer\n pipes={displayDiagram.pipes}\n selectedPipeIds={selectionEnabled ? selection.selectedPipeIds : undefined}\n onPipeClick={undefined}\n enableWaypointEditing={true}\n onWaypointClick={handleWaypointClick}\n onWaypointDragStart={handleWaypointDragStart}\n selectedWaypointPipeId={selectedWaypoint?.pipeId || null}\n selectedWaypointIndex={selectedWaypoint?.pointIndex ?? null}\n editingPipeId={waypointDragState.draggedId?.split(':')[0] || null}\n draggingWaypointIndex={\n waypointDragState.draggedId\n ? parseInt(waypointDragState.draggedId.split(':')[1], 10)\n : null\n }\n waypointDragOffset={waypointDragState.offset}\n onPipeSegmentClick={handlePipeSegmentClick}\n />\n )}\n\n {/* Render telemetry data overlays */}\n {telemetryEnabled && telemetry && (\n <g id=\"layer-telemetry\">\n {displayDiagram.nodes.map((node) => {\n // Status LEDs bind to named signals and show no value badge.\n if (node.symbolId === 'status-indicator') return null;\n\n const binding = telemetry.get(node.id);\n if (!binding) return null;\n\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol) return null;\n\n // Position overlay centered horizontally and below the node by default\n const overlayX = node.transform.x + symbol.viewBox.width / 2;\n const overlayY = node.transform.y + symbol.viewBox.height + 50;\n\n return (\n <DataOverlay\n key={`telemetry-${node.id}`}\n nodeId={node.id}\n x={overlayX}\n y={overlayY}\n telemetry={binding}\n scale={features.telemetryScale ?? 1.0}\n statusColorMap={statusColorMap}\n />\n );\n })}\n </g>\n )}\n\n {/* Drop indicator */}\n {dragOverPosition && (\n <circle\n cx={dragOverPosition.x}\n cy={dragOverPosition.y}\n r={20}\n fill=\"#3b82f6\"\n opacity={0.3}\n stroke=\"#3b82f6\"\n strokeWidth={2}\n pointerEvents=\"none\"\n />\n )}\n\n {/* Connection mode rubber-band line */}\n {isConnecting && connectionSource && connectionCursor && (\n <g id=\"connection-preview\" pointerEvents=\"none\">\n {(() => {\n const sourceNode = displayDiagram.nodes.find((n) => n.id === connectionSource);\n if (!sourceNode) return null;\n\n // If a source port was selected, draw line from that port\n const symbol = getSymbolDefinition(sourceNode.symbolId);\n let lineStartX = sourceNode.transform.x;\n let lineStartY = sourceNode.transform.y;\n\n if (connectionSourcePort && symbol?.ports) {\n const port = symbol.ports.find((p) => p.id === connectionSourcePort);\n if (port) {\n const portPos = getPortWorldPosition(sourceNode, connectionSourcePort);\n if (portPos) {\n lineStartX = portPos.x;\n lineStartY = portPos.y;\n }\n }\n }\n\n return (\n <>\n {/* Highlight source port */}\n <circle\n cx={lineStartX}\n cy={lineStartY}\n r={30}\n fill=\"none\"\n stroke=\"#10b981\"\n strokeWidth={3}\n opacity={0.6}\n />\n {/* Rubber-band line */}\n <line\n x1={lineStartX}\n y1={lineStartY}\n x2={connectionCursor.x}\n y2={connectionCursor.y}\n stroke=\"#10b981\"\n strokeWidth={2}\n strokeDasharray=\"5,5\"\n opacity={0.8}\n />\n {/* Cursor indicator */}\n <circle\n cx={connectionCursor.x}\n cy={connectionCursor.y}\n r={8}\n fill=\"#10b981\"\n opacity={0.6}\n />\n </>\n );\n })()}\n </g>\n )}\n\n {/* Port indicators in connection mode */}\n {features.connectionMode && (\n <g id=\"port-indicators\">\n {displayDiagram.nodes.map((node) => {\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol?.ports) return null;\n\n return symbol.ports.map((port) => {\n const portPos = getPortWorldPosition(node, port.id);\n if (!portPos) return null;\n\n const isHovered = hoveredPort === port.id && hoveredNode === node.id;\n const isSourcePort =\n node.id === connectionSource && port.id === connectionSourcePort;\n\n return (\n <g key={`${node.id}-${port.id}`}>\n {/* Invisible larger hit area for easier clicking */}\n <circle\n cx={portPos.x}\n cy={portPos.y}\n r={15}\n fill=\"transparent\"\n style={{ cursor: 'pointer' }}\n pointerEvents=\"auto\"\n onClick={(e) => {\n e.stopPropagation();\n // Trigger the same logic as clicking on the node\n if (isConnecting && connectionSource) {\n // Second click - complete connection\n if (connectionSource !== node.id) {\n onPipeCreated?.(\n connectionSource,\n node.id,\n connectionSourcePort || undefined,\n port.id\n );\n setIsConnecting(false);\n setConnectionSource(null);\n setConnectionSourcePort(null);\n setConnectionCursor(null);\n setHoveredPort(null);\n }\n } else {\n // First click - start connection\n setIsConnecting(true);\n setConnectionSource(node.id);\n setConnectionSourcePort(port.id);\n }\n }}\n />\n {/* Visible port indicator */}\n <circle\n cx={portPos.x}\n cy={portPos.y}\n r={isHovered ? 8 : 5}\n fill={isSourcePort ? '#10b981' : isHovered ? '#3b82f6' : '#94a3b8'}\n stroke={isSourcePort ? '#059669' : isHovered ? '#2563eb' : '#64748b'}\n strokeWidth={isHovered || isSourcePort ? 2 : 1}\n opacity={isHovered || isSourcePort ? 1 : 0.6}\n pointerEvents=\"none\"\n />\n </g>\n );\n });\n })}\n </g>\n )}\n </svg>\n\n {/* Position Panel - shows when single node is selected */}\n {features.dragNodes && (\n <PositionPanel\n selectedNode={selectedNode}\n selectedWaypoint={\n selectedWaypoint\n ? {\n pipeId: selectedWaypoint.pipeId,\n pointIndex: selectedWaypoint.pointIndex,\n position: localDiagram.pipes.find((p) => p.id === selectedWaypoint.pipeId)\n ?.routePoints[selectedWaypoint.pointIndex] || { x: 0, y: 0 },\n }\n : null\n }\n onPositionChange={handlePositionChange}\n onWaypointPositionChange={handleWaypointPositionChange}\n position=\"top-right\"\n />\n )}\n </div>\n );\n}\n\n/**\n * Memo boundary - see PIDCanvasImpl doc comment (renderer-OOM fix). Shallow\n * compare: `diagram`, `telemetry`, `features`, `style`, `statusColorMap`, and\n * every callback are compared by identity; hosts that keep them stable skip\n * the full-canvas re-render entirely when unrelated screen state changes.\n */\nexport const PIDCanvas = React.memo(PIDCanvasImpl);\n","import React from 'react';\nimport type { NodeInstance } from '../../diagram/schema';\nimport type { NodeBadge, NodeBadgeCorner } from '../../diagram/schema/NodeBadge';\nimport { getSymbolDefinition, getAppearanceDefaults } from '../../diagram/symbols';\nimport { getBadgeGlyph } from '../../diagram/badges';\nimport { resolveStatusColor, normalizeDeviceStatus } from '../../theme/statusColors';\nimport type { StatusColorMap } from '../../theme/statusColors';\n\n// The unknown hatch reproduces the token's CSS equivalent:\n// `repeating-linear-gradient(135deg, transparent 0 5px, #8f8fc255 5px 7px)`.\n// A 7-unit tile holds a 5-unit background run and a 2-unit stripe, so the\n// same rhythm repeats every 7 user units along the diagonal.\nconst HATCH_TILE_SIZE = 7;\nconst HATCH_STRIPE_WIDTH = 2;\nconst HATCH_STRIPE_ANGLE = 135;\n// The token's stripe alpha, 0x55 of 0xff. Applied through `fill-opacity`,\n// not by appending hex digits to the colour. The stroke colour is a consumer\n// override point, so it may arrive as `rgb()`, a named colour, or an 8-digit\n// hex. Concatenating onto any of those yields an invalid colour, and the\n// stripe would silently paint black instead of the consumer's choice.\nconst HATCH_STRIPE_OPACITY = 0x55 / 0xff;\n\n/**\n * True when the system cannot confirm this node's state.\n *\n * Two triggers share one appearance (issue #27):\n * - `status: 'unknown'` marks an untrusted device (OPC UA severity Bad).\n * - `positionUnknown: true` marks a position the control system disclaims.\n *\n * Normalization goes through the shared helper, so this check cannot\n * disagree with the colour the symbol would otherwise be painted with.\n */\nfunction isUnknownNode(node: NodeInstance): boolean {\n return node.positionUnknown === true || normalizeDeviceStatus(node.status) === 'unknown';\n}\n\n// NodeBadge defaults (#33). A badge is a general slot: the consumer picks\n// every property. These are only the fallback when a property is omitted,\n// never a value the library imposes - see NodeBadge.ts for the one property\n// that has NO fallback (blinkPeriodMs).\nconst DEFAULT_BADGE_CORNER: NodeBadgeCorner = 'top-right';\nconst DEFAULT_BADGE_SIZE = 14; // disc diameter, local SVG units (matches the existing \"Forced\" corner badge's r=7 in DataOverlay)\nconst DEFAULT_BADGE_COLOR = '#111827'; // near-black, the TFFi first use\nconst DEFAULT_BADGE_BACKGROUND = '#d1d5db'; // light grey, the TFFi first use\nconst BADGE_GLYPH_BOX = 24; // glyphs are authored centered in a 24x24 local box\n\nconst BADGE_BLINK_KEYFRAME_ID = 'pid-node-badge-blink-keyframes';\nconst BADGE_BLINK_KEYFRAME_NAME = 'pid-node-badge-blink';\n\n/**\n * Injects the badge blink keyframe once into <head>, not per instance or\n * per render.\n *\n * DataOverlay's status dot used to blink via a SMIL `<animate\n * repeatCount=\"indefinite\">` and switched to a CSS keyframe because Blink's\n * SMIL timeline retains an indefinitely-repeating animation as a native\n * \"traced handle\" when its element unmounts mid-animation - a detached-node\n * leak under kiosk Chromium (see DataOverlay.tsx). A badge mounts and\n * unmounts with the condition it marks, so the same risk applies here. The\n * keyframe itself has no duration baked in - each badge's own\n * `blinkPeriodMs` supplies the duration via its inline `animation` style\n * (#33: the library must not hardcode a period).\n */\nfunction ensureBadgeBlinkKeyframes(): void {\n if (typeof document === 'undefined') return; // SSR guard\n if (document.getElementById(BADGE_BLINK_KEYFRAME_ID)) return; // idempotent\n const style = document.createElement('style');\n style.id = BADGE_BLINK_KEYFRAME_ID;\n style.textContent = `@keyframes ${BADGE_BLINK_KEYFRAME_NAME}{0%{opacity:1}50%{opacity:.15}100%{opacity:1}}`;\n document.head.appendChild(style);\n}\n\n/**\n * Badge disc center, in the symbol's own local (pre-transform) coordinates.\n * Centered ON the viewBox corner point, offset outward by a quarter of the\n * disc so most of it clears the symbol's own strokes while still reading as\n * attached to that corner (#33).\n */\nfunction badgeCenter(\n corner: NodeBadgeCorner,\n vb: { width: number; height: number },\n size: number\n): { x: number; y: number } {\n const margin = size / 4;\n switch (corner) {\n case 'top-left':\n return { x: -margin, y: -margin };\n case 'bottom-left':\n return { x: -margin, y: vb.height + margin };\n case 'bottom-right':\n return { x: vb.width + margin, y: vb.height + margin };\n case 'top-right':\n default:\n return { x: vb.width + margin, y: -margin };\n }\n}\n\n/**\n * Renders a node's corner badge (#33).\n *\n * Stacks on top of the symbol and its status color - it is a sibling\n * element, never a replacement for `--symbol-fill`/`--symbol-stroke`, so a\n * node can show `status: 'fault'` and a badge at the same time (the whole\n * reason this is a badge and not a body-color change).\n *\n * The badge counter-rotates like the node label does, so its glyph stays\n * upright regardless of the node's own rotation; its disc position still\n * swings with the node like the label's position does.\n */\nfunction renderNodeBadge(\n badge: NodeBadge,\n vb: { width: number; height: number },\n rotation: number\n): React.ReactElement {\n const glyphDef = getBadgeGlyph(badge.glyph);\n const size = badge.size ?? DEFAULT_BADGE_SIZE;\n const color = badge.color ?? DEFAULT_BADGE_COLOR;\n const backgroundColor = badge.backgroundColor ?? DEFAULT_BADGE_BACKGROUND;\n const corner = badge.corner ?? DEFAULT_BADGE_CORNER;\n const { x: cx, y: cy } = badgeCenter(corner, vb, size);\n const radius = size / 2;\n const glyphScale = size / BADGE_GLYPH_BOX;\n\n let blinkStyle: React.CSSProperties | undefined;\n if (badge.blink) {\n ensureBadgeBlinkKeyframes();\n blinkStyle = {\n animation: `${BADGE_BLINK_KEYFRAME_NAME} ${badge.blinkPeriodMs}ms ease-in-out infinite`,\n };\n }\n\n return (\n <g\n data-node-badge={badge.glyph}\n data-badge-corner={corner}\n data-badge-blink={badge.blink ? 'true' : 'false'}\n aria-label={badge.label}\n role={badge.label ? 'img' : undefined}\n transform={rotation ? `translate(${cx}, ${cy}) rotate(${-rotation})` : `translate(${cx}, ${cy})`}\n style={blinkStyle}\n >\n <circle r={radius} fill={backgroundColor} />\n {glyphDef && (\n <g\n color={color}\n transform={`scale(${glyphScale}) translate(${-BADGE_GLYPH_BOX / 2}, ${-BADGE_GLYPH_BOX / 2})`}\n dangerouslySetInnerHTML={{ __html: glyphDef.svgContent }}\n />\n )}\n </g>\n );\n}\n\nexport interface NodeRendererProps {\n /** Array of node instances to render */\n nodes: NodeInstance[];\n /** IDs of selected nodes */\n selectedNodeIds?: Set<string>;\n /** Callback when a node is clicked */\n onNodeClick?: (nodeId: string, event: React.MouseEvent) => void;\n /** Callback when a node is double-clicked */\n onNodeDoubleClick?: (nodeId: string, event: React.MouseEvent) => void;\n /** Callback when a node drag starts */\n onNodeDragStart?: (nodeId: string, event: React.MouseEvent | React.TouchEvent) => void;\n /** ID of node being dragged */\n draggingNodeId?: string | null;\n /** Drag offset for the dragging node */\n dragOffset?: { x: number; y: number };\n /** Whether dragging is enabled (default: false) */\n enableDrag?: boolean;\n /** Custom status-to-color map (overrides ISA-101 defaults) */\n statusColorMap?: StatusColorMap;\n}\n\n/**\n * NodeRenderer - Renders symbol instances with transforms and selection support\n *\n * Applies translate, rotate, and scale transforms to each node\n * Supports click selection with visual feedback\n *\n * Memoized (renderer-OOM fix): this layer holds the bulk of the SVG (the\n * dangerouslySetInnerHTML symbol bodies) and receives NO telemetry, so on a\n * telemetry-driven canvas re-render every prop is unchanged and the whole\n * layer bails out. Requires the callers' callbacks to be identity-stable\n * (PIDCanvas wraps its node handlers in useCallback for this).\n */\nfunction NodeRendererImpl({\n nodes,\n selectedNodeIds,\n onNodeClick,\n onNodeDoubleClick,\n onNodeDragStart,\n draggingNodeId,\n dragOffset = { x: 0, y: 0 },\n enableDrag = false,\n statusColorMap,\n}: NodeRendererProps): React.ReactElement {\n // A stable id per instance, so two canvases on one page never share or\n // clobber the hatch pattern's id.\n //\n // The id is stripped to letters, digits, underscore and dash. React 18 and\n // React 19 are both peer dependencies, and they return different `useId`\n // shapes: React 18 returns a value with colons, React 19 returns one with\n // underscores. The id goes into a `url(#...)` paint reference, so a\n // punctuation character in it is a risk this strip removes outright.\n const hatchPatternId = `node-unknown-hatch-${React.useId().replace(/[^a-zA-Z0-9_-]/g, '')}`;\n\n const visibleNodes = nodes.filter((node) => node.visible !== false); // Filter hidden nodes\n const hasUnknownNode = visibleNodes.some(isUnknownNode);\n // Resolved once for the whole canvas: the pattern is emitted once per\n // NodeRenderer instance, not once per node, so it needs one colour pair,\n // not each node's own resolved entry.\n const unknownEntry = resolveStatusColor('unknown', statusColorMap);\n\n return (\n <g id=\"layer-nodes\">\n {hasUnknownNode && (\n <defs>\n {/* Periwinkle hatch for an unconfirmed state (issue #27). The flat\n fill alone sits too close to the `off` fill to read as a\n separate state (see statusColors.ts), so the hatch is the\n non-colour signal that makes `unknown` legible. The background\n rect keeps the token's flat fill under the stripe, so the\n symbol still reads as the token colour with stripes on top. */}\n <pattern\n id={hatchPatternId}\n data-testid=\"unknown-hatch-pattern\"\n patternUnits=\"userSpaceOnUse\"\n width={HATCH_TILE_SIZE}\n height={HATCH_TILE_SIZE}\n patternTransform={`rotate(${HATCH_STRIPE_ANGLE})`}\n >\n <rect width={HATCH_TILE_SIZE} height={HATCH_TILE_SIZE} fill={unknownEntry.fill} />\n <rect\n x={HATCH_TILE_SIZE - HATCH_STRIPE_WIDTH}\n width={HATCH_STRIPE_WIDTH}\n height={HATCH_TILE_SIZE}\n fill={unknownEntry.stroke}\n fillOpacity={HATCH_STRIPE_OPACITY}\n />\n </pattern>\n </defs>\n )}\n {visibleNodes.map((node) => {\n const symbol = getSymbolDefinition(node.symbolId);\n const isSelected = selectedNodeIds?.has(node.id) ?? false;\n const isDragging = draggingNodeId === node.id;\n\n if (!symbol) {\n // Symbol not found - render placeholder\n return (\n <g key={node.id}>\n <circle\n cx={node.transform.x}\n cy={node.transform.y}\n r=\"15\"\n fill=\"#ff0000\"\n opacity=\"0.3\"\n stroke=\"#ff0000\"\n strokeWidth=\"2\"\n />\n <text\n x={node.transform.x}\n y={node.transform.y}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n fontSize=\"10\"\n fill=\"#ff0000\"\n >\n ?\n </text>\n </g>\n );\n }\n\n // Build transform string\n const transforms: string[] = [];\n\n // Apply drag offset if this node is being dragged\n const effectiveX = isDragging ? node.transform.x + dragOffset.x : node.transform.x;\n const effectiveY = isDragging ? node.transform.y + dragOffset.y : node.transform.y;\n\n // Translate\n transforms.push(`translate(${effectiveX}, ${effectiveY})`);\n\n // Rotate (around center of symbol)\n if (node.transform.rotation) {\n const centerX = symbol.viewBox.width / 2;\n const centerY = symbol.viewBox.height / 2;\n transforms.push(`rotate(${node.transform.rotation}, ${centerX}, ${centerY})`);\n }\n\n // Scale\n if (node.transform.scale && node.transform.scale !== 1) {\n const centerX = symbol.viewBox.width / 2;\n const centerY = symbol.viewBox.height / 2;\n transforms.push(\n `translate(${centerX}, ${centerY}) scale(${node.transform.scale}) translate(${-centerX}, ${-centerY})`\n );\n }\n\n // Flip\n if (node.transform.flipX || node.transform.flipY) {\n const scaleX = node.transform.flipX ? -1 : 1;\n const scaleY = node.transform.flipY ? -1 : 1;\n const centerX = symbol.viewBox.width / 2;\n const centerY = symbol.viewBox.height / 2;\n transforms.push(\n `translate(${centerX}, ${centerY}) scale(${scaleX}, ${scaleY}) translate(${-centerX}, ${-centerY})`\n );\n }\n\n // Determine colors: unknown hatch > customColors > statusColorMap > ISA-101 defaults.\n //\n // The unknown hatch wins over an explicit `customColors`. Nothing may\n // mask an unconfirmed state with a confident colour. A Status LED\n // gets `customColors.fill` from `useBooleanIndicators`, which reads\n // telemetry; when the device is untrusted, that telemetry is\n // untrusted too, so its colour must not replace the hatch.\n //\n // `--symbol-fill` is a valid target for an SVG `url(#id)` paint\n // reference, so pointing it at the pattern above reaches every\n // symbol without touching a definition.\n const statusEntry = resolveStatusColor(node.status, statusColorMap);\n const isUnknown = isUnknownNode(node);\n const fillColor = isUnknown\n ? `url(#${hatchPatternId})`\n : (node.customColors?.fill ?? statusEntry.fill);\n const strokeColor = isUnknown\n ? unknownEntry.stroke\n : (node.customColors?.stroke ?? statusEntry.stroke);\n\n // Appearance schema (per-element editable properties): schema defaults\n // merged under the node's own metadata overrides.\n const appearance = { ...getAppearanceDefaults(node.symbolId), ...(node.metadata ?? {}) };\n // CSS custom properties + filter, built as a plain record and spread\n // into the group style (custom props are not typed on CSSProperties).\n const ledStyle: Record<string, string | number> = {};\n if (appearance.shape !== undefined) {\n ledStyle['--led-rx'] = appearance.shape === 'circle' ? '50%' : '2';\n ledStyle['--led-rotate'] = appearance.shape === 'diamond' ? '45deg' : '0deg';\n }\n if (appearance.led !== undefined) {\n ledStyle['--led-bezel'] = appearance.led ? '2' : '0';\n }\n if (appearance.glow) {\n // A `url(#...)` paint reference is not a valid drop-shadow colour,\n // so an unknown node glows in the unknown stroke colour instead.\n const glowColor = isUnknown ? unknownEntry.stroke : fillColor;\n ledStyle.filter = `drop-shadow(0 0 4px ${glowColor})`;\n }\n const ledAnimation =\n typeof appearance.animation === 'string' && appearance.animation !== 'none'\n ? appearance.animation\n : undefined;\n\n // Label position (appearance-driven): 'bottom' default matches the\n // previous fixed layout, so unconfigured symbols are unchanged.\n const vb = symbol.viewBox;\n const ox = node.labelOffset?.x ?? 0;\n const oy = node.labelOffset?.y ?? 0;\n const labelPos =\n typeof appearance.labelPosition === 'string' ? appearance.labelPosition : 'bottom';\n let labelX = vb.width / 2 + ox;\n let labelY = vb.height + 12 + oy;\n let labelAnchor: 'start' | 'middle' | 'end' = 'middle';\n if (labelPos === 'top') {\n labelX = vb.width / 2 + ox;\n labelY = -6 + oy;\n labelAnchor = 'middle';\n } else if (labelPos === 'left') {\n labelX = -6 + ox;\n labelY = vb.height / 2 + oy;\n labelAnchor = 'end';\n } else if (labelPos === 'right') {\n labelX = vb.width + 6 + ox;\n labelY = vb.height / 2 + oy;\n labelAnchor = 'start';\n }\n\n return (\n <g\n key={node.id}\n data-node-id={node.id}\n data-symbol-id={node.symbolId}\n data-selected={isSelected}\n data-dragging={isDragging}\n data-led-animation={ledAnimation}\n data-position-unknown={node.positionUnknown === true ? 'true' : undefined}\n transform={transforms.join(' ')}\n style={\n {\n '--symbol-fill': fillColor,\n '--symbol-stroke': strokeColor,\n cursor: enableDrag\n ? isDragging\n ? 'grabbing'\n : 'grab'\n : onNodeClick\n ? 'pointer'\n : 'default',\n ...ledStyle,\n } as React.CSSProperties\n }\n onClick={(e) => {\n // Always fire onClick - drag system will handle whether to drag\n e.stopPropagation();\n onNodeClick?.(node.id, e);\n }}\n onDoubleClick={(e) => {\n e.stopPropagation();\n onNodeDoubleClick?.(node.id, e);\n }}\n onMouseDown={(e) => {\n if (enableDrag) {\n // Don't stop propagation - let click still fire\n onNodeDragStart?.(node.id, e);\n }\n }}\n onTouchStart={(e) => {\n if (enableDrag) {\n e.stopPropagation();\n onNodeDragStart?.(node.id, e);\n }\n }}\n >\n {/* Selection indicator - bounding box */}\n {isSelected && (\n <rect\n x=\"-5\"\n y=\"-5\"\n width={symbol.viewBox.width + 10}\n height={symbol.viewBox.height + 10}\n fill=\"none\"\n stroke={isDragging ? '#10b981' : '#3b82f6'}\n strokeWidth={isDragging ? '3' : '2'}\n strokeDasharray=\"4 2\"\n rx=\"2\"\n pointerEvents=\"none\"\n />\n )}\n\n {/* Render symbol inline (future: use <use> with <defs>) */}\n <g\n dangerouslySetInnerHTML={{ __html: symbol.svgContent }}\n data-symbol-content={symbol.id}\n style={{ pointerEvents: 'auto' }}\n />\n\n {/* Node label */}\n <text\n x={labelX}\n y={labelY}\n textAnchor={labelAnchor}\n fontSize={node.labelFontSize ?? 10}\n fontWeight=\"600\"\n fill={isSelected ? '#3b82f6' : '#1f2937'}\n transform={\n node.transform.rotation\n ? `rotate(${-node.transform.rotation}, ${labelX}, ${labelY})`\n : undefined\n }\n >\n {node.label || node.id}\n </text>\n\n {/* Corner badge (#33): a condition that is NOT a health severity.\n Renders last so it is never occluded by the symbol or label,\n and never touches --symbol-fill/--symbol-stroke above, so it\n stacks on every status, including 'fault'. */}\n {node.badge && renderNodeBadge(node.badge, vb, node.transform.rotation)}\n </g>\n );\n })}\n </g>\n );\n}\n\n/** Memo boundary - see NodeRendererImpl doc comment (renderer-OOM fix). */\nexport const NodeRenderer = React.memo(NodeRendererImpl);\n","import type { SymbolDefinition } from '../schema';\n\n/**\n * Mock symbol library with basic P&ID shapes\n * Phase 4 will add proper symbol library management\n */\nexport const mockSymbolLibrary: Record<string, SymbolDefinition> = {\n 'valve-gate': {\n id: 'valve-gate',\n name: 'Gate Valve (Powered)',\n category: 'valve',\n viewBox: { x: 0, y: 0, width: 26, height: 26 },\n ports: [\n { id: 'inlet', x: 0, y: 18, type: 'inlet', direction: 'in' },\n { id: 'outlet', x: 26, y: 18, type: 'outlet', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Actuator box -->\n <rect x=\"16.74\" y=\"0\" width=\"18.02\" height=\"18.02\" fill=\"var(--symbol-fill, #edeeef)\" stroke=\"var(--symbol-stroke, #666666)\" pointer-events=\"all\"/>\n <!-- Actuator stem -->\n <path d=\"M 25.75 18.02 L 25.75 36.05\" fill=\"none\" stroke=\"var(--symbol-stroke, #666666)\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" pointer-events=\"all\"/>\n <!-- Valve body (two triangles forming diamond) -->\n <path d=\"M 0 20.6 L 25.75 36.05 L 0 51.5 Z M 51.5 20.6 L 25.75 36.05 L 51.5 51.5 Z\" fill=\"var(--symbol-fill, #edeeef)\" stroke=\"var(--symbol-stroke, #666666)\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" pointer-events=\"all\"/>\n </g>\n `,\n metadata: {\n description: 'Gate valve with powered actuator',\n tags: ['valve', 'gate', 'powered', 'actuator'],\n },\n },\n\n 'status-indicator': {\n id: 'status-indicator',\n name: 'Status LED',\n category: 'instrument',\n viewBox: { x: 0, y: 0, width: 24, height: 24 },\n ports: [],\n svgContent: `\n <rect x=\"2\" y=\"2\" width=\"20\" height=\"20\"\n rx=\"var(--led-rx, 50%)\"\n fill=\"var(--symbol-fill, #64748b)\"\n stroke=\"rgba(255,255,255,0.4)\"\n stroke-width=\"var(--led-bezel, 0)\"\n style=\"transform-box: fill-box; transform: rotate(var(--led-rotate, 0deg)); transform-origin: center; pointer-events: all;\"/>\n `,\n metadata: {\n description: 'Status LED indicator driven by telemetry status color',\n tags: ['status', 'led', 'indicator', 'instrument'],\n },\n },\n\n 'pump-positive-displacement': {\n id: 'pump-positive-displacement',\n name: 'Positive Displacement Pump',\n category: 'pump',\n viewBox: { x: 0, y: 0, width: 28.125, height: 28.125 },\n ports: [\n { id: 'inlet', x: 0, y: 14.0625, type: 'inlet', direction: 'in' },\n { id: 'outlet', x: 28.125, y: 14.0625, type: 'outlet', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.28125)\">\n <!-- Outer circle -->\n <ellipse \n cx=\"50\" \n cy=\"50\" \n rx=\"50\" \n ry=\"50\" \n fill=\"var(--symbol-fill, #edeeef)\"\n stroke=\"var(--symbol-stroke, rgb(0, 0, 0))\"\n stroke-width=\"1\"\n />\n \n <!-- Diagonal line from top to bottom-right -->\n <path \n d=\"M 50 0 L 100 50 L 50 100\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, rgb(0, 0, 0))\"\n stroke-width=\"1\"\n stroke-miterlimit=\"10\"\n />\n \n <!-- Center square -->\n <rect \n x=\"35\" \n y=\"35\" \n width=\"30\" \n height=\"30\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, rgb(0, 0, 0))\"\n stroke-width=\"1\"\n />\n </g>\n `,\n metadata: {\n description: 'Positive displacement pump (ISO standard)',\n tags: ['pump', 'positive-displacement', 'iso'],\n },\n },\n\n 'vannefold-bottom': {\n id: 'vannefold-bottom',\n name: 'Vannefold Block (Bottom)',\n category: 'valve',\n viewBox: { x: -0.25, y: -0.25, width: 44, height: 33.892578 },\n ports: [\n { id: 'bottom-inlet', x: 0, y: 12.5, type: 'control', direction: 'in' },\n { id: 'top-outlet', x: 24, y: 32.5, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Bottom inlet pipe -->\n <path \n d=\"M 3,29.785156 H 0 v -10 h 3 m 14,10 h 3 v -10 h -3 m -15,8 h 16 m -16,-6 h 16\"\n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Top outlet pipe -->\n <path \n d=\"m 43,49.785156 v -3 h 10 v 3 m -10,14 v 3 h 10 v -3 m -8,-15 v 16 m 6,-16 v 16\"\n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"7\" \n y=\"0\" \n width=\"80\" \n height=\"59.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"9\" \n y=\"2\" \n width=\"76\" \n height=\"55.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"15\" cy=\"7\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"7\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"7\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"7\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"51.785156\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"51.785156\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"51.785156\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"51.785156\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n </g>\n `,\n metadata: {\n description: 'Vannefold heat exchanger block with bottom inlet configuration',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'bottom'],\n },\n },\n\n 'vannefold-middle': {\n id: 'vannefold-middle',\n name: 'Vannefold Block (Middle)',\n category: 'valve',\n viewBox: { x: -0.25, y: -0.25, width: 47.5, height: 26.548828 },\n ports: [\n { id: 'left-inlet', x: 0, y: 13.65625, type: 'control', direction: 'in' },\n { id: 'right-outlet', x: 47.5, y: 13.65625, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Left inlet pipe -->\n <path \n d=\"M 3,22.3125 H 0 v 10 H 3 m 14,-10 h 3 v 10 h -3 m -15,-8 h 16 m -16,6 h 16\"\n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"7\" \n y=\"0\" \n width=\"80\" \n height=\"52.097656\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"9\" \n y=\"0\" \n width=\"76\" \n height=\"52.097656\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"15\" cy=\"5.3125\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"5.3125\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"5.3125\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"5.3125\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"47.3125\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"47.3125\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"47.3125\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"47.3125\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n </g>\n `,\n metadata: {\n description: 'Vannefold heat exchanger block with side inlet configuration',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'middle'],\n },\n },\n\n 'vannefold-top': {\n id: 'vannefold-top',\n name: 'Vannefold Block (Top)',\n category: 'valve',\n viewBox: { x: -0.25, y: -0.25, width: 47.5, height: 30.392578 },\n ports: [\n { id: 'bottom-inlet', x: 0, y: 17.5, type: 'control', direction: 'in' },\n { id: 'top-outlet', x: 24, y: 0, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Bottom inlet pipe -->\n <path \n d=\"M 3,30 H 0 V 40 H 3 M 17,30 h 3 V 40 H 17 M 2,32 H 18 M 2,38 H 18\"\n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"7\" \n y=\"0\" \n width=\"80\" \n height=\"59.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"9\" \n y=\"2\" \n width=\"76\" \n height=\"57.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"15\" cy=\"8\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"8\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"8\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"8\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"55\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"55\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"55\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"55\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n </g>\n `,\n metadata: {\n description: 'Vannefold heat exchanger block with bottom inlet and top outlet configuration',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'top'],\n },\n },\n\n 'vannefold-top-top-inlet': {\n id: 'vannefold-top-top-inlet',\n name: 'Vannefold Block (Top, Top Inlet)',\n category: 'valve',\n viewBox: { x: 0, y: 0, width: 44, height: 33.788002 },\n ports: [\n { id: 'top-inlet', x: 23.5, y: 0, type: 'control', direction: 'in' },\n { id: 'bottom-outlet', x: 0, y: 21, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Top inlet pipe flange -->\n <path \n d=\"M 52,3 V 1.3598666e-8 H 42 V 3 m 10,14 v 3 H 42 V 17 M 50,2 V 18 M 44,2 v 16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Bottom outlet pipe flange -->\n <path \n d=\"M 3,36.79085 H 0 v 10 h 3 m 14,-10 h 3 v 10 h -3 m -15,-8 h 16 m -16,6 h 16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"7\" \n y=\"6.7908498\" \n width=\"80\" \n height=\"59.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"9\" \n y=\"8.7908498\" \n width=\"76\" \n height=\"57.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"15\" cy=\"14.79085\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"14.79085\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"61.79085\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"61.79085\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"14.79085\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"14.79085\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"61.79085\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"61.79085\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n </g>\n `,\n metadata: {\n description: 'Vannefold heat exchanger block with top inlet and bottom outlet configuration',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'top', 'top-inlet'],\n },\n },\n\n 'vannefold-left': {\n id: 'vannefold-left',\n name: 'Vannefold Block (Left)',\n category: 'valve',\n viewBox: { x: 0, y: 0, width: 33.892578, height: 44.25 },\n ports: [\n { id: 'left-inlet', x: 0, y: 20.75, type: 'control', direction: 'in' },\n { id: 'right-outlet', x: 21, y: 43.5, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Left inlet pipe flange -->\n <path \n d=\"M 3,46.5 H 2.8789924e-8 v -10 H 3 m 14,10 h 3 v -10 h -3 m -15,8 h 16 m -16,-6 h 16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Right outlet pipe flange -->\n <path \n d=\"m 37,70.5 v -3 h 10 v 3 m -10,14 v 3 h 10 v -3 m -8,-15 v 16 m 6,-16 v 16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"7\" \n y=\"0.5\" \n width=\"59.785156\" \n height=\"80\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"9\" \n y=\"2.5\" \n width=\"57.785156\" \n height=\"76\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"15\" cy=\"8.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"8.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"62\" cy=\"8.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"62\" cy=\"8.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"72.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"72.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"62\" cy=\"72.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"62\" cy=\"72.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n `,\n metadata: {\n description: 'Vannefold heat exchanger block with left inlet and right outlet, vertical orientation',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'left', 'vertical'],\n },\n },\n \n 'vannefold-right': {\n id: 'vannefold-right',\n name: 'Vannefold Block (Right)',\n category: 'valve',\n viewBox: { x: 0, y: 0, width: 33.892578, height: 44.25 },\n ports: [\n { id: 'right-inlet', x: 33.892578, y: 20.75, type: 'control', direction: 'in' },\n { id: 'left-outlet', x: 12.392578, y: 43.75, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Right inlet pipe flange -->\n <path \n d=\"m 63.785156,46.5 h 3 v -10 h -3 m -14,10 h -3 v -10 h 3 m 15,8 h -16 m 16,-6 h -16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Left outlet pipe flange -->\n <path \n d=\"m 29.785156,70.5 v -3 h -10 v 3 m 10,14 v 3 h -10 v -3 m 8,-15 v 16 m -6,-16 v 16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"0\" \n y=\"0.5\" \n width=\"59.785156\" \n height=\"80\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"0\" \n y=\"2.5\" \n width=\"57.785156\" \n height=\"76\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"51.785156\" cy=\"8.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"51.785156\" cy=\"8.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"4.785156\" cy=\"8.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"4.785156\" cy=\"8.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"51.785156\" cy=\"72.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"51.785156\" cy=\"72.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"4.785156\" cy=\"72.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"4.785156\" cy=\"72.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n `,\n metadata: {\n description: 'Vannefold heat exchanger block with right inlet and left outlet, vertical orientation',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'right', 'vertical'],\n },\n },\n\n 'pixer': {\n id: 'pixer',\n name: 'Pixer',\n category: 'pump',\n viewBox: { x: 0, y: 0, width: 25.3125, height: 23.34375 },\n ports: [\n { id: 'right-outlet', x: 25.3125, y: 15.46875, type: 'control', direction: 'out' },\n { id: 'left-outlet', x: 0, y: 15.46875, type: 'control', direction: 'out' },\n { id: 'left-inlet', x: 25.3125, y: 9.84375, type: 'control', direction: 'in' },\n { id: 'right-inlet', x: 0, y: 9.84375, type: 'control', direction: 'in' },\n { id: 'top-inlet', x: 12.65625, y: 0, type: 'control', direction: 'in' },\n ],\n svgContent: `\n <g transform=\"scale(0.0984375) translate(-195, -186)\">\n <!-- Top mounting flange -->\n <rect \n width=\"141.313\" \n height=\"9.872\" \n transform=\"translate(252.392 185.89)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Motor housing top -->\n <rect \n width=\"106.614\" \n height=\"2.468\" \n transform=\"translate(269.742 199.918)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"106.614\" \n height=\"5.835\" \n transform=\"translate(269.742 202.384)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"106.614\" \n height=\"2.468\" \n transform=\"translate(269.742 208.222)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Bottom mounting flange -->\n <rect \n width=\"221.702\" \n height=\"2.401\" \n transform=\"translate(212.199 395.587)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"221.702\" \n height=\"6.269\" \n transform=\"translate(212.199 397.988)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"221.702\" \n height=\"2.65\" \n transform=\"translate(212.199 404.257)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Motor housing transition -->\n <path \n d=\"M444.7,228.776l17.117,4.1H568.429l17.089-4.085a.032.032,0,0,0,0-.062H444.705a.028.028,0,0,0-.007.046Z\" \n transform=\"translate(-192.059 -32.959)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Bottom flange -->\n <rect \n width=\"256\" \n height=\"9.872\" \n transform=\"translate(195.05 411.066)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n <path \n d=\"M195.05,1148.286l17.149-4.156H433.9l17.149,4.156Z\" \n transform=\"translate(0 -737.221)\" \n fill=\"var(--symbol-fill, #e2e2e2)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n \n <!-- Vessel top cone -->\n <path \n d=\"M451.05,328.356h-256l74.692-34.946H376.358Z\" \n transform=\"translate(0 -82.72)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Main vessel body -->\n <rect \n width=\"256\" \n height=\"59.803\" \n transform=\"translate(195.05 245.636)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n <rect \n width=\"256\" \n height=\"76.892\" \n transform=\"translate(195.05 308.417)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Vessel bottom transitions -->\n <path \n d=\"M449.265,705.7H196.773l-1.654-1.465a.014.014,0,0,1,0-.023H451.078Z\" \n transform=\"translate(-0.049 -398.769)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <path \n d=\"M449.278,710.66H196.793l-1.663,1.462v.016H451.086s.012,0,0-.016Z\" \n transform=\"translate(-0.062 -403.731)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <path \n d=\"M433.9,1060.758H212.2L195.05,1050.48h256Z\" \n transform=\"translate(0 -665.171)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Agitator blades/impellers (simplified without gradients) -->\n <ellipse \n cx=\"15.454\" \n cy=\"6.096\" \n rx=\"15.454\" \n ry=\"6.096\" \n transform=\"translate(307.486 228.161)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #c0c0c0)\"\n />\n <ellipse \n cx=\"15.454\" \n cy=\"5.3\" \n rx=\"15.454\" \n ry=\"5.3\" \n transform=\"translate(307.486 229.753)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <ellipse \n cx=\"5.174\" \n cy=\"15.117\" \n rx=\"5.174\" \n ry=\"15.117\" \n transform=\"translate(367.144 236.079) rotate(-77.61)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M947,380.271s1.631-2.83,8.084-2.235c5.427.5,16.927,2.855,20.574,8.26\" \n transform=\"translate(-578.512 -147.764)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"15.117\" \n cy=\"5.174\" \n rx=\"15.117\" \n ry=\"5.174\" \n transform=\"translate(247 232.438) rotate(-12.39)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M456.439,380.271s-1.631-2.83-8.073-2.235c-5.43.5-16.93,2.855-20.576,8.26\" \n transform=\"translate(-179.058 -147.764)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"1.651\" \n cy=\"15.082\" \n rx=\"1.651\" \n ry=\"15.082\" \n transform=\"translate(406.71 230.174) rotate(-68.13)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M1119,375.62a119.876,119.876,0,0,1,26.193,10.419\" \n transform=\"translate(-710.84 -145.969)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"15.082\" \n cy=\"1.651\" \n rx=\"15.082\" \n ry=\"1.651\" \n transform=\"translate(210.225 238.342) rotate(-21.87)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M293.943,375.62a119.829,119.829,0,0,0-26.193,10.419\" \n transform=\"translate(-55.932 -145.969)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n </g>\n `,\n metadata: {\n description: 'Pixer mixer/agitator vessel with top-mounted drive',\n tags: ['vessel', 'mixer', 'agitator', 'pixer', 'tank'],\n },\n },\n\n 'pixer-barb': {\n id: 'pixer-barb',\n name: 'Pixer (Hose Barb)',\n category: 'pump',\n viewBox: { x: 0, y: 0, width: 25.3125, height: 23.34375 },\n ports: [\n { id: 'right-outlet', x: 25.3125, y: 15.46875, type: 'control', direction: 'out' },\n { id: 'left-outlet', x: 0, y: 15.46875, type: 'control', direction: 'out' },\n { id: 'left-inlet', x: 25.3125, y: 9.84375, type: 'control', direction: 'in' },\n { id: 'right-inlet', x: 0, y: 9.84375, type: 'control', direction: 'in' },\n { id: 'top-inlet', x: 12.65625, y: 0, type: 'control', direction: 'in' },\n ],\n svgContent: `\n <g transform=\"scale(0.0984375) translate(-195, -186)\">\n <!-- Hose barb ridges at top -->\n <rect \n width=\"20\" \n height=\"2\" \n transform=\"translate(313 173)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"0.5\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <rect \n width=\"20\" \n height=\"2\" \n transform=\"translate(313 177)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"0.5\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <rect \n width=\"20\" \n height=\"2\" \n transform=\"translate(313 181)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"0.5\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n \n <!-- Taper from barb to motor housing -->\n <path \n d=\"M 313,183 L 269.742,199.918 L 376.356,199.918 L 333,183 Z\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n \n <!-- Motor housing top -->\n <rect \n width=\"106.614\" \n height=\"2.468\" \n transform=\"translate(269.742 199.918)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"106.614\" \n height=\"5.835\" \n transform=\"translate(269.742 202.384)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"106.614\" \n height=\"2.468\" \n transform=\"translate(269.742 208.222)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Bottom mounting flange -->\n <rect \n width=\"221.702\" \n height=\"2.401\" \n transform=\"translate(212.199 395.587)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"221.702\" \n height=\"6.269\" \n transform=\"translate(212.199 397.988)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"221.702\" \n height=\"2.65\" \n transform=\"translate(212.199 404.257)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Bottom flange -->\n <rect \n width=\"256\" \n height=\"9.872\" \n transform=\"translate(195.05 411.066)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n <path \n d=\"M195.05,1148.286l17.149-4.156H433.9l17.149,4.156Z\" \n transform=\"translate(0 -737.221)\" \n fill=\"var(--symbol-fill, #e2e2e2)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n \n <!-- Vessel top cone -->\n <path \n d=\"M451.05,328.356h-256l74.692-34.946H376.358Z\" \n transform=\"translate(0 -82.72)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Main vessel body -->\n <rect \n width=\"256\" \n height=\"59.803\" \n transform=\"translate(195.05 245.636)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n <rect \n width=\"256\" \n height=\"76.892\" \n transform=\"translate(195.05 308.417)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Vessel bottom transitions -->\n <path \n d=\"M449.265,705.7H196.773l-1.654-1.465a.014.014,0,0,1,0-.023H451.078Z\" \n transform=\"translate(-0.049 -398.769)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <path \n d=\"M449.278,710.66H196.793l-1.663,1.462v.016H451.086s.012,0,0-.016Z\" \n transform=\"translate(-0.062 -403.731)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <path \n d=\"M433.9,1060.758H212.2L195.05,1050.48h256Z\" \n transform=\"translate(0 -665.171)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Agitator blades/impellers (simplified without gradients) -->\n <ellipse \n cx=\"15.454\" \n cy=\"6.096\" \n rx=\"15.454\" \n ry=\"6.096\" \n transform=\"translate(307.486 228.161)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #c0c0c0)\"\n />\n <ellipse \n cx=\"15.454\" \n cy=\"5.3\" \n rx=\"15.454\" \n ry=\"5.3\" \n transform=\"translate(307.486 229.753)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <ellipse \n cx=\"5.174\" \n cy=\"15.117\" \n rx=\"5.174\" \n ry=\"15.117\" \n transform=\"translate(367.144 236.079) rotate(-77.61)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M947,380.271s1.631-2.83,8.084-2.235c5.427.5,16.927,2.855,20.574,8.26\" \n transform=\"translate(-578.512 -147.764)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"15.117\" \n cy=\"5.174\" \n rx=\"15.117\" \n ry=\"5.174\" \n transform=\"translate(247 232.438) rotate(-12.39)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M456.439,380.271s-1.631-2.83-8.073-2.235c-5.43.5-16.93,2.855-20.576,8.26\" \n transform=\"translate(-179.058 -147.764)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"1.651\" \n cy=\"15.082\" \n rx=\"1.651\" \n ry=\"15.082\" \n transform=\"translate(406.71 230.174) rotate(-68.13)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M1119,375.62a119.876,119.876,0,0,1,26.193,10.419\" \n transform=\"translate(-710.84 -145.969)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"15.082\" \n cy=\"1.651\" \n rx=\"15.082\" \n ry=\"1.651\" \n transform=\"translate(210.225 238.342) rotate(-21.87)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M293.943,375.62a119.829,119.829,0,0,0-26.193,10.419\" \n transform=\"translate(-55.932 -145.969)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n </g>\n `,\n metadata: {\n description: 'Pixer mixer/agitator vessel with tapered hose barb connection at top',\n tags: ['vessel', 'mixer', 'agitator', 'pixer', 'tank', 'hose-barb'],\n },\n },\n \n 'vessel-balloon': {\n id: 'vessel-balloon',\n name: 'Vessel',\n category: 'vessel',\n viewBox: { x: 0, y: 0, width: 25.3125, height: 45.5 },\n ports: [\n { id: 'top', type: 'inlet', x: 12.65625, y: 0 },\n { id: 'left', type: 'inlet', x: 2.5, y: 19.5 },\n { id: 'right', type: 'outlet', x: 22.8125, y: 19.5 },\n { id: 'bottom', type: 'outlet', x: 12.65625, y: 45.5 },\n ],\n svgContent: `\n <g transform=\"scale(0.0984375) translate(-195, -100)\">\n <!-- Top opening nozzle -->\n <rect \n width=\"65\" \n height=\"19.5\" \n transform=\"translate(290.5, 100)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Transition from nozzle to dome -->\n <path \n d=\"M 290.5,119.5 L 238,152 L 408,152 L 355.5,119.5 Z\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Upper balloon dome - rounded bulge -->\n <path \n d=\"M 238,152 C 185,210 150,320 180,395 L 466,395 C 496,320 461,210 408,152 Z\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Tapered vessel body -->\n <path \n d=\"M 180,395 L 269.742,530 L 376.356,530 L 466,395 Z\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Bottom cone -->\n <path \n d=\"M 269.742,530 L 280,555 L 366,555 L 376.356,530 Z\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Bottom flange matching pixer top dimensions -->\n <rect \n width=\"141.313\" \n height=\"9.872\" \n transform=\"translate(252.392, 555)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n </g>\n `,\n metadata: {\n description: 'Balloon-shaped vessel with rounded top and tapered bottom, designed to fit on standard pixer',\n tags: ['vessel', 'storage', 'tank', 'balloon'],\n },\n },\n\n 'hollow-fiber': {\n id: 'hollow-fiber',\n name: 'Hollow Fiber',\n category: 'other',\n viewBox: { x: 0, y: 0, width: 30, height: 109 },\n ports: [\n { id: 'top-left', type: 'outlet', x: 10, y: 2.75 },\n { id: 'top-right', type: 'outlet', x: 24.5, y: 22.25 },\n { id: 'bottom-left', type: 'inlet', x: 10, y: 106.25 },\n { id: 'bottom-right', type: 'inlet', x: 24.5, y: 87.25 },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Top left flange -->\n <rect x=\"10.5\" y=\"0.5\" width=\"20\" height=\"10\" rx=\"1.5\" ry=\"1.5\" fill=\"var(--symbol-fill, #eaeaea)\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n <!-- Top right flange -->\n <rect x=\"39.5\" y=\"39.5\" width=\"20\" height=\"10\" rx=\"1.5\" ry=\"1.5\" fill=\"var(--symbol-fill, #eaeaea)\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n <!-- Bottom left flange -->\n <rect x=\"10.5\" y=\"207.5\" width=\"20\" height=\"10\" rx=\"1.5\" ry=\"1.5\" fill=\"var(--symbol-fill, #eaeaea)\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n <!-- Bottom right flange -->\n <rect x=\"39.5\" y=\"169.5\" width=\"20\" height=\"10\" rx=\"1.5\" ry=\"1.5\" fill=\"var(--symbol-fill, #eaeaea)\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n <!-- Main vessel body -->\n <rect x=\"0.5\" y=\"9.5\" width=\"40\" height=\"199.5\" rx=\"6\" ry=\"6\" fill=\"var(--symbol-fill, #eaeaea)\" stroke=\"var(--symbol-stroke, #000000)\" />\n </g>\n `,\n metadata: {\n description: 'Hollow fiber filter module with multiple inlet/outlet connections',\n tags: ['filter', 'hollow-fiber', 'filtration', 'separation', 'membrane'],\n },\n },\n \n 'psr-f': {\n id: 'psr-f',\n name: 'PSR F',\n category: 'instrument',\n viewBox: { x: 0, y: 0, width: 28.5, height: 28.5 },\n ports: [\n { id: 'top', type: 'signal', x: 14.25, y: 7.125 },\n { id: 'right', type: 'signal', x: 21.375, y: 14.25 },\n { id: 'bottom', type: 'signal', x: 14.25, y: 21.375 },\n { id: 'left', type: 'signal', x: 7.125, y: 14.25 },\n ],\n svgContent: `\n <g transform=\"scale(0.25) translate(28.5, 28.5)\">\n <!-- Circle background -->\n <ellipse \n cx=\"28.5\" \n cy=\"28.5\" \n rx=\"28.5\" \n ry=\"28.5\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #ffffff)\"\n />\n \n <!-- Letter F -->\n <text \n x=\"28.5\" \n y=\"38\" \n text-anchor=\"middle\" \n font-family=\"'Times New Roman', Georgia, serif\" \n font-size=\"34\" \n font-weight=\"normal\" \n font-style=\"italic\" \n fill=\"var(--symbol-stroke, #000000)\"\n >F</text>\n </g>\n `,\n metadata: {\n description: 'PSR F instrument symbol - Flow indicator',\n tags: ['instrument', 'psr', 'flow', 'indicator'],\n },\n },\n \n 'psr-p': {\n id: 'psr-p',\n name: 'PSR P',\n category: 'instrument',\n viewBox: { x: 0, y: 0, width: 28.5, height: 28.5 },\n ports: [\n { id: 'top', type: 'signal', x: 14.25, y: 7.125 },\n { id: 'right', type: 'signal', x: 21.375, y: 14.25 },\n { id: 'bottom', type: 'signal', x: 14.25, y: 21.375 },\n { id: 'left', type: 'signal', x: 7.125, y: 14.25 },\n ],\n svgContent: `\n <g transform=\"scale(0.25) translate(28.5, 28.5)\">\n <!-- Circle background -->\n <ellipse \n cx=\"28.5\" \n cy=\"28.5\" \n rx=\"28.5\" \n ry=\"28.5\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #ffffff)\"\n />\n \n <!-- Letter P -->\n <text \n x=\"28.5\" \n y=\"38\" \n text-anchor=\"middle\" \n font-family=\"'Times New Roman', Georgia, serif\" \n font-size=\"34\" \n font-weight=\"normal\" \n font-style=\"italic\" \n fill=\"var(--symbol-stroke, #000000)\"\n >P</text>\n </g>\n `,\n metadata: {\n description: 'PSR P instrument symbol - Pressure indicator',\n tags: ['instrument', 'psr', 'pressure', 'indicator'],\n },\n },\n};\n\n/**\n * Custom symbol registry for user-defined symbols\n */\nconst customSymbols: Record<string, SymbolDefinition> = {};\n\n/**\n * Register a custom symbol definition\n * @param symbol - The symbol definition to register\n * @example\n * registerSymbol({\n * id: 'my-custom-valve',\n * name: 'My Custom Valve',\n * category: 'valve',\n * viewBox: { x: 0, y: 0, width: 100, height: 100 },\n * ports: [...],\n * svgContent: '...',\n * });\n */\nexport function registerSymbol(symbol: SymbolDefinition): void {\n customSymbols[symbol.id] = symbol;\n}\n\n/**\n * Register multiple custom symbols at once\n * @param symbols - Array of symbol definitions or a record of symbols\n */\nexport function registerSymbols(symbols: SymbolDefinition[] | Record<string, SymbolDefinition>): void {\n if (Array.isArray(symbols)) {\n symbols.forEach((symbol) => registerSymbol(symbol));\n } else {\n Object.values(symbols).forEach((symbol) => registerSymbol(symbol));\n }\n}\n\n/**\n * Get symbol definition by ID (checks both mock library and custom symbols)\n */\nexport function getSymbolDefinition(symbolId: string): SymbolDefinition | undefined {\n return customSymbols[symbolId] ?? mockSymbolLibrary[symbolId];\n}\n\n/**\n * Get all available symbol IDs (includes both mock library and custom symbols)\n */\nexport function getAvailableSymbols(): string[] {\n return [...Object.keys(customSymbols), ...Object.keys(mockSymbolLibrary)];\n}\n","import type { NodeInstance } from '../schema';\n\n/**\n * Appearance schema — the reusable contract for per-element editable properties.\n *\n * A placeable element declares its editable appearance fields as data. The\n * properties panel (NodeConfigPanel) renders any schema generically, and the\n * renderer (NodeRenderer) reads the same defaults. Add a new element by adding\n * one schema entry — no panel code changes. This is the pattern the\n * \"add a placeable element\" skill generates.\n */\n\nexport type AppearanceFieldKind = 'select' | 'boolean' | 'number' | 'text' | 'tag';\n\n/**\n * Where a field's value is read from and written to on the NodeInstance.\n * - 'metadata' (default): the untyped node.metadata bag, keyed by field.key\n * - 'visible': the node.visible flag\n * - 'scale': node.transform.scale (via valueMap for select options)\n */\nexport type AppearanceTarget = 'metadata' | 'visible' | 'scale';\n\nexport interface AppearanceField {\n /** metadata key, or the driver for the special 'visible'/'scale' targets */\n key: string;\n kind: AppearanceFieldKind;\n /** Human label for the panel; defaults to key when absent */\n label?: string;\n /** Options for kind 'select' */\n options?: string[];\n /** Where the value is stored; defaults to 'metadata' */\n target?: AppearanceTarget;\n /** Default value when the node has no value set */\n default?: string | number | boolean;\n /** For target 'scale': maps a select option to a numeric scale factor */\n valueMap?: Record<string, number>;\n /** Optional help text shown under the control in the panel */\n help?: string;\n}\n\nexport type AppearanceValue = string | number | boolean;\n\n/** Named color presets for the boolean indicator (name → hex). */\nexport const COLOR_PRESETS: Record<string, string> = {\n green: '#22c55e',\n red: '#ef4444',\n amber: '#f59e0b',\n blue: '#3b82f6',\n grey: '#64748b',\n};\n\n/** Resolve a color preset name to a hex value, falling back to grey. */\nexport function resolvePresetColor(name: string | undefined): string {\n return (name && COLOR_PRESETS[name]) || COLOR_PRESETS.grey;\n}\n\nconst COLOR_OPTIONS = Object.keys(COLOR_PRESETS);\n\n/** Appearance schema for the Status LED symbol. */\nexport const STATUS_LED_APPEARANCE: AppearanceField[] = [\n {\n key: 'shape',\n kind: 'select',\n label: 'Shape',\n options: ['circle', 'square', 'diamond'],\n default: 'circle',\n },\n {\n key: 'size',\n kind: 'select',\n label: 'Size',\n target: 'scale',\n options: ['small', 'medium', 'large'],\n default: 'medium',\n valueMap: { small: 0.75, medium: 1, large: 1.5 },\n },\n { key: 'led', kind: 'boolean', label: 'Bezel ring', default: true },\n { key: 'glow', kind: 'boolean', label: 'Diffusion (glow)', default: false },\n {\n key: 'animation',\n kind: 'select',\n label: 'Animation',\n options: ['none', 'pulse', 'blink'],\n default: 'none',\n },\n {\n key: 'labelPosition',\n kind: 'select',\n label: 'Label position',\n options: ['bottom', 'top', 'left', 'right'],\n default: 'bottom',\n },\n // Color binding: a named signal drives the on/off color.\n {\n key: 'colorSource',\n kind: 'tag',\n label: 'Color source (tag)',\n default: '',\n help: 'Signal/tag that drives color. True (non-zero) shows On color; false shows Off color. Blank = unbound (grey).',\n },\n {\n key: 'onColor',\n kind: 'select',\n label: 'On color (true)',\n options: COLOR_OPTIONS,\n default: 'green',\n help: 'Color when the Color source is true.',\n },\n {\n key: 'offColor',\n kind: 'select',\n label: 'Off color (false)',\n options: COLOR_OPTIONS,\n default: 'red',\n help: 'Color when the Color source is false.',\n },\n // Visibility binding: an independent named signal drives show/hide.\n {\n key: 'visibleSource',\n kind: 'tag',\n label: 'Visibility source (tag)',\n default: '',\n help: 'Signal/tag that drives visibility. True shows the LED, false hides it. Blank = always visible (use the Visible checkbox).',\n },\n {\n key: 'visible',\n kind: 'boolean',\n label: 'Visible',\n target: 'visible',\n default: true,\n help: 'Static show/hide, used only when no Visibility source is set.',\n },\n];\n\n/** Registry of symbol id → appearance schema. Add one entry per placeable element. */\nconst APPEARANCE_REGISTRY: Record<string, AppearanceField[]> = {\n 'status-indicator': STATUS_LED_APPEARANCE,\n};\n\n/** Return the editable appearance fields for a symbol, or [] if it declares none. */\nexport function getAppearanceSchema(symbolId: string): AppearanceField[] {\n return APPEARANCE_REGISTRY[symbolId] ?? [];\n}\n\n/**\n * Default values for the 'metadata'-targeted fields of a symbol, keyed by field key.\n * The renderer merges these under the node's own metadata, so a freshly dropped\n * element looks correct before any editing.\n */\nexport function getAppearanceDefaults(symbolId: string): Record<string, AppearanceValue> {\n const out: Record<string, AppearanceValue> = {};\n for (const field of getAppearanceSchema(symbolId)) {\n if ((field.target ?? 'metadata') === 'metadata' && field.default !== undefined) {\n out[field.key] = field.default;\n }\n }\n return out;\n}\n\n/** Read the current value of a field from a node, applying the field default. */\nexport function readAppearanceValue(node: NodeInstance, field: AppearanceField): AppearanceValue {\n if (field.target === 'visible') {\n return node.visible ?? (field.default as boolean | undefined) ?? true;\n }\n if (field.target === 'scale') {\n const scale = node.transform.scale ?? 1;\n if (field.valueMap) {\n let bestOption = field.options?.[0] ?? String(scale);\n let bestDiff = Infinity;\n for (const [option, value] of Object.entries(field.valueMap)) {\n const diff = Math.abs(value - scale);\n if (diff < bestDiff) {\n bestDiff = diff;\n bestOption = option;\n }\n }\n return bestOption;\n }\n return scale;\n }\n const value = node.metadata?.[field.key];\n return (value as AppearanceValue | undefined) ?? (field.default as AppearanceValue);\n}\n\n/** Produce the NodeInstance update for a new field value. */\nexport function writeAppearanceValue(\n node: NodeInstance,\n field: AppearanceField,\n value: AppearanceValue\n): Partial<NodeInstance> {\n if (field.target === 'visible') {\n return { visible: Boolean(value) };\n }\n if (field.target === 'scale') {\n const scale = field.valueMap ? (field.valueMap[String(value)] ?? 1) : Number(value);\n return { transform: { ...node.transform, scale } };\n }\n return { metadata: { ...node.metadata, [field.key]: value } };\n}\n","/**\n * Registry of badge glyphs for the `NodeBadge` corner slot (#33).\n *\n * Mirrors `diagram/symbols/mockSymbolLibrary`'s registration pattern: a\n * built-in set plus a consumer-registered set, looked up by id. A consumer\n * calls `registerBadgeGlyph` from its own app code to add a glyph the\n * library does not ship - the same escape hatch `registerSymbol` already\n * gives P&ID symbols. This is deliberately NOT a string resolved only\n * inside the library (the mistake `Overlay.content.component` made, per\n * #33): the consumer supplies the real markup.\n */\nexport interface BadgeGlyphDefinition {\n /** Id a `NodeBadge.glyph` references. */\n id: string;\n /**\n * Raw SVG markup, drawn centered in a 24x24 local box (0,0 to 24,24).\n * Use `stroke=\"currentColor\"` / `fill=\"currentColor\"` so the glyph picks\n * up `NodeBadge.color` - the glyph carries no color of its own, only shape.\n */\n svgContent: string;\n}\n\n/** A circle with a slash: the prohibition sign the TFFi application needs first. */\nconst PROHIBIT_GLYPH: BadgeGlyphDefinition = {\n id: 'prohibit',\n svgContent:\n '<circle cx=\"12\" cy=\"12\" r=\"9\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\"/>' +\n '<line x1=\"5.6\" y1=\"18.4\" x2=\"18.4\" y2=\"5.6\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\"/>',\n};\n\nconst builtInBadgeGlyphs: Record<string, BadgeGlyphDefinition> = {\n [PROHIBIT_GLYPH.id]: PROHIBIT_GLYPH,\n};\n\nconst customBadgeGlyphs: Record<string, BadgeGlyphDefinition> = {};\n\n/** Register a single custom badge glyph, or replace a built-in one by reusing its id. */\nexport function registerBadgeGlyph(glyph: BadgeGlyphDefinition): void {\n customBadgeGlyphs[glyph.id] = glyph;\n}\n\n/** Register multiple custom badge glyphs at once. */\nexport function registerBadgeGlyphs(\n glyphs: BadgeGlyphDefinition[] | Record<string, BadgeGlyphDefinition>\n): void {\n if (Array.isArray(glyphs)) {\n glyphs.forEach((glyph) => registerBadgeGlyph(glyph));\n } else {\n Object.values(glyphs).forEach((glyph) => registerBadgeGlyph(glyph));\n }\n}\n\n/** Get a badge glyph definition by id (checks custom glyphs before built-ins). */\nexport function getBadgeGlyph(glyphId: string): BadgeGlyphDefinition | undefined {\n return customBadgeGlyphs[glyphId] ?? builtInBadgeGlyphs[glyphId];\n}\n\n/** List every registered badge glyph id (built-in plus custom). */\nexport function getAvailableBadgeGlyphs(): string[] {\n return [...Object.keys(customBadgeGlyphs), ...Object.keys(builtInBadgeGlyphs)];\n}\n","import React from 'react';\nimport type { PipeEdge, PipeStyle } from '../../diagram/schema';\n\nexport interface PipeRendererProps {\n /** Array of pipe edges to render */\n pipes: PipeEdge[];\n /** IDs of selected pipes */\n selectedPipeIds?: Set<string>;\n /** Callback when a pipe is clicked */\n onPipeClick?: (pipeId: string, event: React.MouseEvent) => void;\n /** Enable waypoint editing mode */\n enableWaypointEditing?: boolean;\n /** Callback when waypoint drag starts */\n onWaypointDragStart?: (\n pipeId: string,\n pointIndex: number,\n event: React.MouseEvent | React.TouchEvent\n ) => void;\n /** Callback when waypoint is clicked (for selection) */\n onWaypointClick?: (pipeId: string, pointIndex: number) => void;\n /** Selected waypoint pipe ID */\n selectedWaypointPipeId?: string | null;\n /** Selected waypoint point index */\n selectedWaypointIndex?: number | null;\n /** ID of pipe being edited */\n editingPipeId?: string | null;\n /** Index of waypoint being dragged */\n draggingWaypointIndex?: number | null;\n /** Offset for dragged waypoint */\n waypointDragOffset?: { x: number; y: number };\n /** Callback when pipe segment is clicked (to add waypoint) */\n onPipeSegmentClick?: (\n pipeId: string,\n position: { x: number; y: number },\n segmentIndex: number\n ) => void;\n}\n\n/**\n * PipeRenderer - Renders pipe connections between nodes with selection support\n *\n * Uses SVG polyline elements to render pipes with route points\n * Supports click selection with visual feedback\n *\n * Memoized (renderer-OOM fix): receives no telemetry, so on a telemetry-driven\n * canvas re-render every prop is unchanged and the pipe layer bails out.\n * Callers must pass identity-stable callbacks (PIDCanvas useCallback-wraps\n * its pipe handlers for this).\n */\nfunction PipeRendererImpl({\n pipes,\n selectedPipeIds,\n onPipeClick,\n enableWaypointEditing = false,\n onWaypointDragStart,\n onWaypointClick,\n selectedWaypointPipeId,\n selectedWaypointIndex,\n editingPipeId,\n draggingWaypointIndex,\n waypointDragOffset,\n onPipeSegmentClick,\n}: PipeRendererProps): React.ReactElement {\n return (\n <g id=\"layer-pipes\">\n {pipes\n .filter((pipe) => pipe.visible !== false) // Filter hidden pipes\n .map((pipe) => {\n const isSelected = selectedPipeIds?.has(pipe.id) ?? false;\n const isEditing = enableWaypointEditing && editingPipeId === pipe.id;\n const showEditingControls = enableWaypointEditing && isSelected;\n\n // Apply waypoint drag offset if dragging\n const displayPoints = pipe.routePoints.map((point, idx) => {\n if (isEditing && draggingWaypointIndex === idx && waypointDragOffset) {\n return {\n x: point.x + waypointDragOffset.x,\n y: point.y + waypointDragOffset.y,\n };\n }\n return point;\n });\n\n // Convert route points to polyline points string\n const pointsString = displayPoints.map((point) => `${point.x},${point.y}`).join(' ');\n\n // Apply pipe style with defaults\n const pipeStyle: Partial<PipeStyle> = pipe.style || {};\n const stroke = isSelected ? '#3b82f6' : pipeStyle.stroke || '#94a1b8';\n const strokeWidth = isSelected\n ? (pipeStyle.strokeWidth || 3) + 2\n : pipeStyle.strokeWidth || 3;\n const strokeDasharray = pipeStyle.strokeDasharray;\n const opacity = pipeStyle.opacity !== undefined ? pipeStyle.opacity : 1;\n\n return (\n <g\n key={pipe.id}\n data-pipe-id={pipe.id}\n data-selected={isSelected}\n onClick={(e) => {\n e.stopPropagation();\n onPipeClick?.(pipe.id, e);\n }}\n style={{\n cursor: onPipeClick ? 'pointer' : 'default',\n }}\n >\n {/* Only render pipe lines when NOT in waypoint-only mode */}\n {!enableWaypointEditing && (\n <>\n {/* Selection indicator - wider transparent clickable area */}\n {onPipeClick && (\n <polyline\n points={pointsString}\n fill=\"none\"\n stroke=\"transparent\"\n strokeWidth={strokeWidth + 8}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n pointerEvents=\"stroke\"\n />\n )}\n\n {/* Main pipe line */}\n <polyline\n points={pointsString}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n strokeDasharray={strokeDasharray}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n opacity={opacity}\n data-from={pipe.fromNodeId}\n data-to={pipe.toNodeId}\n pointerEvents={onPipeClick ? 'none' : 'auto'}\n />\n </>\n )}\n\n {/* Connection points indicators */}\n {displayPoints.map((point, idx) => {\n // Show all waypoints including endpoints\n const isEndpoint = idx === 0 || idx === displayPoints.length - 1;\n const isSelectedWaypoint =\n selectedWaypointPipeId === pipe.id && selectedWaypointIndex === idx;\n\n const isDragging = isEditing && draggingWaypointIndex === idx;\n const waypointSize = isSelectedWaypoint\n ? 8\n : isDragging\n ? 8\n : showEditingControls\n ? 6\n : isSelected\n ? 3\n : 2;\n const waypointOpacity = isSelectedWaypoint\n ? 1\n : isDragging\n ? 1\n : showEditingControls\n ? 0.9\n : isSelected\n ? 0.8\n : 0.5;\n\n // Color priority: selected > dragging > endpoint > default\n const waypointColor = isSelectedWaypoint\n ? '#3b82f6' // Blue for selected\n : isEndpoint && showEditingControls\n ? '#f59e0b' // Amber for endpoints\n : isDragging\n ? '#10b981' // Green for dragging\n : stroke; // Default pipe color\n\n return (\n <circle\n key={`${pipe.id}-point-${idx}`}\n cx={point.x}\n cy={point.y}\n r={waypointSize}\n fill={waypointColor}\n stroke={showEditingControls ? '#ffffff' : 'none'}\n strokeWidth={showEditingControls ? 2 : 0}\n opacity={waypointOpacity}\n className=\"pipe-route-point\"\n style={{\n cursor: showEditingControls ? (isDragging ? 'grabbing' : 'grab') : 'default',\n }}\n pointerEvents={showEditingControls ? 'all' : 'none'}\n onClick={(e) => {\n if (showEditingControls) {\n e.stopPropagation();\n onWaypointClick?.(pipe.id, idx);\n }\n }}\n onMouseDown={(e) => {\n if (showEditingControls) {\n e.stopPropagation();\n onWaypointDragStart?.(pipe.id, idx, e);\n }\n }}\n onTouchStart={(e) => {\n if (showEditingControls) {\n e.stopPropagation();\n onWaypointDragStart?.(pipe.id, idx, e);\n }\n }}\n />\n );\n })}\n\n {/* Pipe segment click areas (for adding waypoints) */}\n {showEditingControls && onPipeSegmentClick && displayPoints.length >= 2 && (\n <>\n {displayPoints.slice(0, -1).map((point, idx) => {\n const nextPoint = displayPoints[idx + 1];\n const midX = (point.x + nextPoint.x) / 2;\n const midY = (point.y + nextPoint.y) / 2;\n\n return (\n <g key={`${pipe.id}-segment-${idx}`}>\n {/* Larger clickable area */}\n <circle\n cx={midX}\n cy={midY}\n r={8}\n fill=\"transparent\"\n className=\"pipe-segment-add-area\"\n style={{ cursor: 'crosshair' }}\n pointerEvents=\"all\"\n onClick={(e) => {\n e.stopPropagation();\n onPipeSegmentClick?.(pipe.id, { x: midX, y: midY }, idx);\n }}\n />\n {/* Visible indicator */}\n <circle\n cx={midX}\n cy={midY}\n r={4}\n fill=\"#3b82f6\"\n stroke=\"#ffffff\"\n strokeWidth={2}\n opacity={0.6}\n className=\"pipe-segment-add\"\n pointerEvents=\"none\"\n />\n </g>\n );\n })}\n </>\n )}\n\n {/* Pipe label (optional) */}\n {pipe.showLabel !== false && pipe.label && pipe.routePoints.length >= 2 && (\n <text\n x={\n pipe.routePoints[Math.floor(pipe.routePoints.length / 2)].x +\n (pipe.labelOffset?.x ?? 0)\n }\n y={\n pipe.routePoints[Math.floor(pipe.routePoints.length / 2)].y -\n 8 +\n (pipe.labelOffset?.y ?? 0)\n }\n textAnchor=\"middle\"\n fontSize=\"10\"\n fontWeight={isSelected ? '600' : '500'}\n fill={stroke}\n opacity={opacity * 0.8}\n pointerEvents=\"none\"\n >\n {pipe.label}\n </text>\n )}\n </g>\n );\n })}\n </g>\n );\n}\n\n/** Memo boundary - see PipeRendererImpl doc comment (renderer-OOM fix). */\nexport const PipeRenderer = React.memo(PipeRendererImpl);\n","import React from 'react';\nimport type { TelemetryBinding, TelemetryStatus } from '../../diagram/schema/TelemetryBinding';\nimport { resolveStatusColor, normalizeDeviceStatus, FORCED_MARK_COLOR } from '../../theme/statusColors';\nimport type { StatusColorMap } from '../../theme/statusColors';\nimport { UnknownStatusGlyph } from '../../theme/unknownStatusGlyph';\n\n/**\n * The status dot's pulse used to be a SMIL `<animate repeatCount=\"indefinite\">`.\n * SMIL animations with an indefinite repeat are retained by Blink's SMIL\n * timeline (as native \"traced handles\") when their element is removed from the\n * DOM mid-animation — the classic Chromium detached-node leak. DataOverlay\n * mounts/unmounts this dot every time a node's status crosses a threshold\n * (i.e. up to once per telemetry tick in a live system), so the orphaned\n * animated <circle>s accumulate monotonically. A CSS keyframe animation is\n * torn down by Blink on element removal, so we drive the pulse from CSS\n * instead. The keyframe is injected once into <head>, not per instance.\n */\nconst PULSE_KEYFRAME_ID = 'pid-data-overlay-pulse-keyframes';\nconst PULSE_ANIMATION = 'pid-data-overlay-pulse 1.5s ease-in-out infinite';\n\nfunction ensurePulseKeyframes(): void {\n if (typeof document === 'undefined') return; // SSR guard\n if (document.getElementById(PULSE_KEYFRAME_ID)) return; // idempotent\n const style = document.createElement('style');\n style.id = PULSE_KEYFRAME_ID;\n style.textContent =\n '@keyframes pid-data-overlay-pulse{0%{opacity:.9}50%{opacity:.3}100%{opacity:.9}}';\n document.head.appendChild(style);\n}\n\nfunction renderStatusDot(\n cx: number,\n cy: number,\n scale: number,\n fill: string,\n status: TelemetryStatus\n): React.ReactElement {\n // ISA-18.2 reserves blinking for an unacknowledged alarm (KTD5). An\n // `unknown` value is not an alarm, so its dot stays solid: no pulse.\n const isUnknown = normalizeDeviceStatus(status) === 'unknown';\n if (!isUnknown) ensurePulseKeyframes();\n return (\n <circle\n className=\"pid-data-overlay-status-dot\"\n cx={cx + 35 * scale}\n cy={cy - 18 * scale}\n r={3 * scale}\n fill={fill}\n style={isUnknown ? undefined : { animation: PULSE_ANIMATION }}\n />\n );\n}\n\nexport interface DataOverlayProps {\n /** Node ID this overlay is for */\n nodeId: string;\n /** X position in SVG coordinates */\n x: number;\n /** Y position in SVG coordinates */\n y: number;\n /** Telemetry binding data */\n telemetry: TelemetryBinding;\n /** Scale factor for display size (default: 1.0) */\n scale?: number;\n /** Custom status-to-color map (overrides ISA-101 defaults) */\n statusColorMap?: StatusColorMap;\n}\n\n/**\n * DataOverlay - Renders live telemetry values above P&ID nodes\n *\n * Shows:\n * - Current value with unit\n * - Status-based color coding\n * - Optional label\n *\n * Memoized (renderer-OOM fix): a live P&ID re-renders the canvas ~1Hz, and\n * every overlay re-rendering every tick - text re-layout, drop-shadow rect,\n * sparkline path recompute - forces recurring large-area SVG rasterization.\n * Under kiosk Chromium's --disable-gpu software compositor that leaks\n * /dev/shm tile buffers until the renderer OOMs. All props except `telemetry`\n * and `statusColorMap` are primitives derived from node geometry, so when the\n * host preserves binding identity for unchanged entries, an unchanged overlay\n * bails out of the re-render entirely.\n */\nfunction DataOverlayImpl({\n nodeId,\n x,\n y,\n telemetry,\n scale = 1.0,\n statusColorMap,\n}: DataOverlayProps): React.ReactElement {\n const { value, display, history, scaling, forced } = telemetry;\n const isForced = forced?.active === true;\n const hasRealValue = isForced && forced?.realValue !== undefined;\n const showValue = display?.showValue ?? true;\n const showStatus = display?.showStatus ?? true;\n const showSparkline = display?.showSparkline ?? false;\n const label = display?.label;\n // Scaling metadata (#1663) is the authoritative source when present:\n // decimals -> precision, units -> unit label, eu range -> sparkline axis.\n // Falls back to the static display.precision / value.unit when absent.\n const precision = scaling?.decimals ?? display?.precision;\n const unit = scaling?.units ?? value.unit;\n const sparklineColor = display?.sparklineColor;\n const textColor = display?.textColor;\n const backgroundColor = display?.backgroundColor;\n const offsetX = display?.offsetX ?? 0;\n const offsetY = display?.offsetY ?? 0;\n\n // Calculate sparkline path if history is provided. Memoized so a change to\n // value/status alone does not redo the per-point path math; keyed on the\n // history ARRAY identity, which the host only replaces when it samples a new\n // point (renderer-OOM fix). Placed before the showValue early-return to keep\n // hook order unconditional.\n const sparklinePath = React.useMemo(() => {\n if (!showSparkline || !history || history.length < 2) return '';\n\n const width = 70 * scale;\n const height = 20 * scale;\n const padding = 5 * scale;\n const effectiveWidth = width - padding * 2;\n const effectiveHeight = height - padding * 2;\n\n // Plot against the EU range when scaling metadata provides one (a fixed,\n // meaningful trend axis), else fall back to the history min/max (#1663).\n const hasEuRange =\n scaling?.euLow !== undefined &&\n scaling?.euHigh !== undefined &&\n scaling.euHigh !== scaling.euLow;\n const min = hasEuRange ? (scaling.euLow as number) : Math.min(...history);\n const max = hasEuRange ? (scaling.euHigh as number) : Math.max(...history);\n const range = max - min || 1; // Avoid division by zero\n\n const points = history.map((val, i) => {\n const xPos = (i / (history.length - 1)) * effectiveWidth + padding;\n const yPos = height - padding - ((val - min) / range) * effectiveHeight;\n return `${xPos.toFixed(2)},${yPos.toFixed(2)}`;\n });\n\n return `M ${points.join(' L ')}`;\n }, [showSparkline, history, scaling, scale]);\n\n if (!showValue) {\n return <></>;\n }\n\n // Format value based on type and precision\n const formatValue = (val: number | string | boolean): string => {\n if (typeof val === 'number') {\n return precision !== undefined ? val.toFixed(precision) : val.toString();\n }\n if (typeof val === 'boolean') {\n return val ? 'ON' : 'OFF';\n }\n return val.toString();\n };\n\n // Resolve status colors: per-binding override > statusColorMap > ISA-101 defaults\n const resolvedEntry = resolveStatusColor(value.status, statusColorMap);\n const customColor = telemetry.statusColors?.[value.status];\n const statusColors = customColor\n ? { bg: customColor, text: '#ffffff', border: customColor }\n : { bg: resolvedEntry.bg, text: resolvedEntry.text, border: resolvedEntry.border };\n\n // Apply explicit backgroundColor override if provided (and not 'status')\n const colors =\n backgroundColor && backgroundColor !== 'status'\n ? { bg: backgroundColor, text: textColor || '#ffffff', border: backgroundColor }\n : statusColors;\n\n // Override text color if explicitly set\n const finalTextColor = textColor || colors.text;\n\n const formattedValue = formatValue(value.value);\n const displayText = unit ? `${formattedValue} ${unit}` : formattedValue;\n const realDisplayText = hasRealValue\n ? unit\n ? `REAL ${formatValue(forced!.realValue as number | string | boolean)} ${unit}`\n : `REAL ${formatValue(forced!.realValue as number | string | boolean)}`\n : undefined;\n\n // A forced value is not a health status (#3607): the border marks it\n // independently of the status-driven fill, so a forced-but-normal reading\n // never reads as an alarm and an alarming forced reading still shows its\n // status color.\n const borderColor = isForced ? FORCED_MARK_COLOR.border : colors.border;\n const borderWidth = (isForced ? 2 : 1) * scale;\n\n const hasSparkline = showSparkline && sparklinePath;\n // The FORCED mark is a corner badge (below) and costs no extra row. Only\n // the REAL value line, when present, needs one.\n const extraMarkRows = realDisplayText ? 1 : 0;\n const overlayHeight =\n (label ? (hasSparkline ? 55 : 30) : hasSparkline ? 47 : 22) * scale + extraMarkRows * 9 * scale;\n const overlayWidth = 80 * scale;\n\n // Decide \"is this value untrusted?\" once, through the shared normalization\n // path. A local string compare here could disagree with the colour the\n // badge is painted with once an alias changes.\n const isUnknownValue = normalizeDeviceStatus(value.status) === 'unknown';\n\n // Apply position offsets\n const displayX = x + offsetX;\n const displayY = y + offsetY;\n\n return (\n <g data-overlay-node={nodeId} data-status={value.status} data-forced={isForced}>\n {/* Background rect */}\n <rect\n x={displayX - overlayWidth / 2}\n y={displayY - 25 * scale}\n width={overlayWidth}\n height={overlayHeight}\n rx={4 * scale}\n fill={colors.bg}\n stroke={borderColor}\n strokeWidth={borderWidth}\n opacity=\"0.95\"\n filter=\"drop-shadow(0 2px 4px rgba(0,0,0,0.2))\"\n />\n\n {/* Label (optional) */}\n {label && (\n <text\n x={displayX}\n y={displayY - 15 * scale}\n textAnchor=\"middle\"\n fontSize={8 * scale}\n fontWeight=\"600\"\n fill={finalTextColor}\n opacity=\"0.9\"\n >\n {label}\n </text>\n )}\n\n {/* Value text */}\n <text\n x={displayX}\n y={label ? displayY - 3 * scale : displayY - 9 * scale}\n textAnchor=\"middle\"\n fontSize={11 * scale}\n fontWeight=\"700\"\n fill={finalTextColor}\n letterSpacing=\"0.02em\"\n >\n {displayText}\n </text>\n\n {/* Status indicator dot (optional).\n Pulse is driven by a CSS keyframe (not SMIL) so Blink releases the\n element when React unmounts it — see ensurePulseKeyframes above. */}\n {showStatus &&\n value.status !== 'normal' &&\n renderStatusDot(displayX, displayY, scale, finalTextColor, value.status)}\n\n {/* Unknown-status glyph (R9, R10, R11): a non-colour marker for a value\n the backend does not trust. One shared module draws it (KTD10), so\n the marker cannot drift from StatusIndicator or DeviceControlPanel.\n Placed in user units, scaled like every other element in this file.\n Gated on showStatus for the same reason the dot is: a caller that\n turned the status channel off does not expect either marker.\n\n Placed on the LEFT corner. The forced-value badge (#26) occupies the\n right corner at the same height, and a value can be both forced and\n untrusted at once. The two markers answer different questions, so\n they must stay separately readable rather than overlap. */}\n {showStatus && isUnknownValue && (\n <UnknownStatusGlyph\n x={displayX - overlayWidth / 2 + 2 * scale}\n y={displayY - 25 * scale - 6 * scale}\n width={12 * scale}\n height={12 * scale}\n />\n )}\n\n {/* Quality indicator (if provided and < 100) */}\n {value.quality !== undefined && value.quality < 100 && (\n <text\n x={displayX}\n y={displayY + 5 * scale}\n textAnchor=\"middle\"\n fontSize={7 * scale}\n fontWeight=\"500\"\n fill={finalTextColor}\n opacity=\"0.7\"\n >\n Q: {value.quality}%\n </text>\n )}\n\n {/* Forced-value marking (#3607, R19). A corner glyph badge, not a\n spelled-out word, so it reads at a glance and costs no extra row.\n A filled circle, not colored text, so it stays legible against\n every status background, including alarm red. Sized to remain\n readable on a 15\" panel-mount screen from a meter away, not\n shrunk purely to save space. */}\n {isForced && (\n <g\n transform={`translate(${displayX + overlayWidth / 2}, ${displayY - 25 * scale})`}\n aria-label=\"Forced\"\n >\n <circle\n r={7 * scale}\n fill={FORCED_MARK_COLOR.bg}\n stroke={FORCED_MARK_COLOR.border}\n strokeWidth={1.5 * scale}\n />\n <text\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n fontSize={8 * scale}\n fontWeight=\"700\"\n fill={FORCED_MARK_COLOR.text}\n >\n F\n </text>\n </g>\n )}\n\n {/* Real (unforced) value, shown beside the forced one (R19). Absent\n until the backend publishes it (alphinity-tffi unit U3b); the\n corner badge above still renders on its own until then. */}\n {realDisplayText && (\n <text\n x={displayX}\n y={displayY + (value.quality !== undefined && value.quality < 100 ? 14 : 5) * scale}\n textAnchor=\"middle\"\n fontSize={7 * scale}\n fontWeight=\"500\"\n fill={finalTextColor}\n opacity=\"0.85\"\n >\n {realDisplayText}\n </text>\n )}\n\n {/* Sparkline trend (if history provided) */}\n {hasSparkline && (\n <g transform={`translate(${displayX - 35 * scale}, ${displayY + (label ? 5 : 0) * scale})`}>\n <path\n d={sparklinePath}\n fill=\"none\"\n stroke={sparklineColor || finalTextColor}\n strokeWidth={1.5 * scale}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n opacity=\"0.9\"\n />\n </g>\n )}\n </g>\n );\n}\n\n/**\n * Memo boundary: bail out when nothing this overlay renders changed. Shallow\n * prop compare is exactly right here - `telemetry` (the binding object) and\n * `statusColorMap` are compared by identity, everything else is a primitive.\n * The host contract (documented on DataOverlayImpl) is that binding identity\n * only changes when displayed content changed.\n */\nexport const DataOverlay = React.memo(DataOverlayImpl);\n","import React, { useState, useEffect } from 'react';\nimport type { NodeInstance, Point } from '../../diagram/schema';\n\nexport interface PositionPanelProps {\n /** The selected node to display/edit position for */\n selectedNode?: NodeInstance | null;\n /** The selected waypoint to display/edit position for */\n selectedWaypoint?: { pipeId: string; pointIndex: number; position: Point } | null;\n /** Callback when node position is changed */\n onPositionChange?: (nodeId: string, x: number, y: number) => void;\n /** Callback when waypoint position is changed */\n onWaypointPositionChange?: (pipeId: string, pointIndex: number, x: number, y: number) => void;\n /** Position of the panel (default: top-right) */\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n}\n\n/**\n * PositionPanel - Displays and allows editing of selected node or waypoint position\n */\nexport function PositionPanel({\n selectedNode,\n selectedWaypoint,\n onPositionChange,\n onWaypointPositionChange,\n position = 'top-right',\n}: PositionPanelProps): React.ReactElement | null {\n const [x, setX] = useState<string>('');\n const [y, setY] = useState<string>('');\n\n // Update inputs when selected node or waypoint changes\n useEffect(() => {\n if (selectedNode) {\n setX(selectedNode.transform.x.toFixed(2));\n setY(selectedNode.transform.y.toFixed(2));\n } else if (selectedWaypoint) {\n setX(selectedWaypoint.position.x.toFixed(2));\n setY(selectedWaypoint.position.y.toFixed(2));\n }\n }, [selectedNode, selectedWaypoint]);\n\n if (!selectedNode && !selectedWaypoint) {\n return null;\n }\n\n const handleXChange = (value: string) => {\n setX(value);\n const numValue = parseFloat(value);\n if (!isNaN(numValue)) {\n if (selectedNode && onPositionChange) {\n onPositionChange(selectedNode.id, numValue, selectedNode.transform.y);\n } else if (selectedWaypoint && onWaypointPositionChange) {\n onWaypointPositionChange(\n selectedWaypoint.pipeId,\n selectedWaypoint.pointIndex,\n numValue,\n selectedWaypoint.position.y\n );\n }\n }\n };\n\n const handleYChange = (value: string) => {\n setY(value);\n const numValue = parseFloat(value);\n if (!isNaN(numValue)) {\n if (selectedNode && onPositionChange) {\n onPositionChange(selectedNode.id, selectedNode.transform.x, numValue);\n } else if (selectedWaypoint && onWaypointPositionChange) {\n onWaypointPositionChange(\n selectedWaypoint.pipeId,\n selectedWaypoint.pointIndex,\n selectedWaypoint.position.x,\n numValue\n );\n }\n }\n };\n\n const handleBlur = () => {\n // Reset to actual position on blur (handles invalid input)\n if (selectedNode) {\n setX(selectedNode.transform.x.toFixed(2));\n setY(selectedNode.transform.y.toFixed(2));\n } else if (selectedWaypoint) {\n setX(selectedWaypoint.position.x.toFixed(2));\n setY(selectedWaypoint.position.y.toFixed(2));\n }\n };\n\n const positionStyles: Record<string, React.CSSProperties> = {\n 'top-left': { top: 10, left: 10 },\n 'top-right': { top: 10, right: 10 },\n 'bottom-left': { bottom: 10, left: 10 },\n 'bottom-right': { bottom: 10, right: 10 },\n };\n\n return (\n <div\n style={{\n position: 'absolute',\n ...positionStyles[position],\n backgroundColor: 'white',\n border: '1px solid #ccc',\n borderRadius: '4px',\n padding: '12px',\n boxShadow: '0 2px 8px rgba(0,0,0,0.1)',\n zIndex: 1000,\n minWidth: '180px',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n fontSize: '13px',\n }}\n >\n <div style={{ marginBottom: '8px', fontWeight: 600, color: '#333' }}>Position</div>\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <label style={{ width: '16px', color: '#666' }}>X:</label>\n <input\n type=\"number\"\n value={x}\n onChange={(e) => handleXChange(e.target.value)}\n onBlur={handleBlur}\n style={{\n flex: 1,\n padding: '4px 8px',\n border: '1px solid #ddd',\n borderRadius: '3px',\n fontSize: '13px',\n }}\n step=\"1\"\n />\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <label style={{ width: '16px', color: '#666' }}>Y:</label>\n <input\n type=\"number\"\n value={y}\n onChange={(e) => handleYChange(e.target.value)}\n onBlur={handleBlur}\n style={{\n flex: 1,\n padding: '4px 8px',\n border: '1px solid #ddd',\n borderRadius: '3px',\n fontSize: '13px',\n }}\n step=\"1\"\n />\n </div>\n </div>\n <div style={{ marginTop: '8px', fontSize: '11px', color: '#888' }}>\n Use arrow keys to nudge (±1px)\n </div>\n </div>\n );\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { ViewBox } from '../schema';\n\nexport interface UseViewBoxOptions {\n /** Initial viewBox configuration */\n initialViewBox: ViewBox;\n /** Enable pan interaction (default: true) */\n enablePan?: boolean;\n /** Enable zoom interaction (default: true) */\n enableZoom?: boolean;\n /** Minimum zoom level (smaller = more zoomed in, default: 0.1) */\n minZoom?: number;\n /** Maximum zoom level (larger = more zoomed out, default: 10) */\n maxZoom?: number;\n /** Zoom sensitivity (default: 0.001) */\n zoomSensitivity?: number;\n /** Allow left-click drag for panning (default: true for touch-friendly) */\n allowLeftClickPan?: boolean;\n}\n\nexport interface UseViewBoxResult {\n /** Current viewBox state */\n viewBox: ViewBox;\n /** Reset viewBox to initial state */\n resetViewBox: () => void;\n /** Zoom in (decrease viewBox size) */\n zoomIn: () => void;\n /** Zoom out (increase viewBox size) */\n zoomOut: () => void;\n /** Fit viewBox to content bounds with optional padding */\n fitToContent: (bounds: { minX: number; minY: number; maxX: number; maxY: number }, padding?: number) => void;\n /** Set viewBox directly */\n setViewBox: (viewBox: ViewBox) => void;\n /** Convert screen coordinates to world (SVG) coordinates */\n screenToWorld: (screenX: number, screenY: number) => { x: number; y: number };\n /** SVG element ref to attach event listeners */\n svgRef: React.RefObject<SVGSVGElement | null>;\n}\n\n/**\n * Custom hook for managing ViewBox state with pan and zoom interactions\n * \n * Pan controls:\n * - Left mouse button + drag (if allowLeftClickPan=true, default)\n * - Middle mouse button + drag (always)\n * - Space + left mouse button + drag (always)\n * - Touch drag (single finger)\n * \n * Zoom controls:\n * - Mouse wheel (zoom to cursor position)\n * - Pinch gesture (touch devices)\n * - Programmatic zoomIn/zoomOut functions\n */\nexport function useViewBox(options: UseViewBoxOptions): UseViewBoxResult {\n const {\n initialViewBox,\n enablePan = true,\n enableZoom = true,\n minZoom = 0.1,\n maxZoom = 10,\n zoomSensitivity = 0.001,\n allowLeftClickPan = true,\n } = options;\n\n const [viewBox, setViewBox] = useState<ViewBox>(initialViewBox);\n const svgRef = useRef<SVGSVGElement | null>(null);\n const isPanningRef = useRef(false);\n const lastMousePosRef = useRef({ x: 0, y: 0 });\n const spaceKeyDownRef = useRef(false);\n const lastTouchDistanceRef = useRef<number | null>(null);\n\n // Reset to initial viewBox\n const resetViewBox = useCallback(() => {\n setViewBox(initialViewBox);\n }, [initialViewBox]);\n\n // Zoom in (25% closer)\n const zoomIn = useCallback(() => {\n setViewBox((prev) => {\n const currentZoom = initialViewBox.width / prev.width;\n const newZoom = Math.min(1 / minZoom, currentZoom * 1.25);\n const scale = initialViewBox.width / newZoom;\n \n const centerX = prev.x + prev.width / 2;\n const centerY = prev.y + prev.height / 2;\n const newWidth = scale;\n const newHeight = (prev.height / prev.width) * scale;\n\n return {\n x: centerX - newWidth / 2,\n y: centerY - newHeight / 2,\n width: newWidth,\n height: newHeight,\n };\n });\n }, [initialViewBox, minZoom]);\n\n // Zoom out (25% farther)\n const zoomOut = useCallback(() => {\n setViewBox((prev) => {\n const currentZoom = initialViewBox.width / prev.width;\n const newZoom = Math.max(1 / maxZoom, currentZoom * 0.8);\n const scale = initialViewBox.width / newZoom;\n \n const centerX = prev.x + prev.width / 2;\n const centerY = prev.y + prev.height / 2;\n const newWidth = scale;\n const newHeight = (prev.height / prev.width) * scale;\n\n return {\n x: centerX - newWidth / 2,\n y: centerY - newHeight / 2,\n width: newWidth,\n height: newHeight,\n };\n });\n }, [initialViewBox, maxZoom]);\n\n // Fit to content bounds\n const fitToContent = useCallback((bounds: { minX: number; minY: number; maxX: number; maxY: number }, padding: number = 50) => {\n const contentWidth = bounds.maxX - bounds.minX;\n const contentHeight = bounds.maxY - bounds.minY;\n \n if (contentWidth === 0 || contentHeight === 0) {\n // No content or invalid bounds\n return;\n }\n\n // Add padding\n const paddedWidth = contentWidth + padding * 2;\n const paddedHeight = contentHeight + padding * 2;\n \n // Maintain aspect ratio of initial viewBox\n const aspectRatio = initialViewBox.width / initialViewBox.height;\n const contentAspectRatio = paddedWidth / paddedHeight;\n \n let newWidth: number;\n let newHeight: number;\n \n if (contentAspectRatio > aspectRatio) {\n // Content is wider - fit to width\n newWidth = paddedWidth;\n newHeight = paddedWidth / aspectRatio;\n } else {\n // Content is taller - fit to height\n newHeight = paddedHeight;\n newWidth = paddedHeight * aspectRatio;\n }\n \n const centerX = bounds.minX + contentWidth / 2;\n const centerY = bounds.minY + contentHeight / 2;\n \n setViewBox({\n x: centerX - newWidth / 2,\n y: centerY - newHeight / 2,\n width: newWidth,\n height: newHeight,\n });\n }, [initialViewBox]);\n\n // Handle mouse wheel zoom\n useEffect(() => {\n if (!enableZoom || !svgRef.current) return;\n\n const svg = svgRef.current;\n\n const handleWheel = (e: WheelEvent) => {\n e.preventDefault();\n\n // Get mouse position in SVG coordinate space\n const rect = svg.getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n\n setViewBox((prev) => {\n // Convert mouse position to viewBox coordinates\n const svgX = prev.x + (mouseX / rect.width) * prev.width;\n const svgY = prev.y + (mouseY / rect.height) * prev.height;\n\n // Calculate zoom factor\n const delta = -e.deltaY * zoomSensitivity;\n const zoom = Math.exp(delta);\n\n // Calculate new viewBox dimensions\n let newWidth = prev.width / zoom;\n let newHeight = prev.height / zoom;\n\n // Clamp zoom level\n const currentZoom = initialViewBox.width / prev.width;\n const newZoom = currentZoom * zoom;\n if (newZoom < minZoom || newZoom > 1 / minZoom) {\n return prev; // Don't zoom beyond limits\n }\n\n // Adjust viewBox position to zoom towards mouse cursor\n const newX = svgX - (mouseX / rect.width) * newWidth;\n const newY = svgY - (mouseY / rect.height) * newHeight;\n\n return {\n x: newX,\n y: newY,\n width: newWidth,\n height: newHeight,\n };\n });\n };\n\n svg.addEventListener('wheel', handleWheel, { passive: false });\n return () => svg.removeEventListener('wheel', handleWheel);\n }, [enableZoom, zoomSensitivity, minZoom, initialViewBox]);\n\n // Handle panning\n useEffect(() => {\n if (!enablePan || !svgRef.current) return;\n\n const svg = svgRef.current;\n\n const handleMouseDown = (e: MouseEvent) => {\n // Check if clicking on a draggable element (node, waypoint, pipe segment)\n const target = e.target as Element;\n const isDraggableNode = target.closest('[data-node-id]');\n const isWaypoint = target.classList.contains('pipe-route-point');\n const isPipeSegment = target.classList.contains('pipe-segment-add') || target.classList.contains('pipe-segment-add-area');\n \n if (isDraggableNode || isWaypoint || isPipeSegment) {\n return; // Let drag handler take over\n }\n\n // Left button (if allowed), middle button, or space + left button\n const isLeftClick = e.button === 0 && allowLeftClickPan;\n const isMiddleClick = e.button === 1;\n const isSpaceLeftClick = e.button === 0 && spaceKeyDownRef.current;\n \n if (isLeftClick || isMiddleClick || isSpaceLeftClick) {\n e.preventDefault();\n isPanningRef.current = true;\n lastMousePosRef.current = { x: e.clientX, y: e.clientY };\n svg.style.cursor = 'grabbing';\n }\n };\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!isPanningRef.current) return;\n\n const dx = e.clientX - lastMousePosRef.current.x;\n const dy = e.clientY - lastMousePosRef.current.y;\n lastMousePosRef.current = { x: e.clientX, y: e.clientY };\n\n setViewBox((prev) => {\n const rect = svg.getBoundingClientRect();\n const scaleX = prev.width / rect.width;\n const scaleY = prev.height / rect.height;\n\n return {\n ...prev,\n x: prev.x - dx * scaleX,\n y: prev.y - dy * scaleY,\n };\n });\n };\n\n const handleMouseUp = () => {\n if (isPanningRef.current) {\n isPanningRef.current = false;\n svg.style.cursor = (allowLeftClickPan || spaceKeyDownRef.current) ? 'grab' : '';\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.code === 'Space' && !spaceKeyDownRef.current) {\n e.preventDefault();\n spaceKeyDownRef.current = true;\n if (!isPanningRef.current && !allowLeftClickPan) {\n svg.style.cursor = 'grab';\n }\n }\n };\n\n const handleKeyUp = (e: KeyboardEvent) => {\n if (e.code === 'Space') {\n spaceKeyDownRef.current = false;\n if (!isPanningRef.current && !allowLeftClickPan) {\n svg.style.cursor = '';\n }\n }\n };\n\n // Set initial cursor if left-click pan is enabled\n if (allowLeftClickPan && !isPanningRef.current) {\n svg.style.cursor = 'grab';\n }\n\n svg.addEventListener('mousedown', handleMouseDown);\n window.addEventListener('mousemove', handleMouseMove);\n window.addEventListener('mouseup', handleMouseUp);\n window.addEventListener('keydown', handleKeyDown);\n window.addEventListener('keyup', handleKeyUp);\n\n return () => {\n svg.removeEventListener('mousedown', handleMouseDown);\n window.removeEventListener('mousemove', handleMouseMove);\n window.removeEventListener('mouseup', handleMouseUp);\n window.removeEventListener('keydown', handleKeyDown);\n window.removeEventListener('keyup', handleKeyUp);\n };\n }, [enablePan, allowLeftClickPan]);\n\n // Handle touch gestures (pan and pinch-to-zoom)\n useEffect(() => {\n if (!svgRef.current) return;\n if (!enablePan && !enableZoom) return;\n\n const svg = svgRef.current;\n\n const handleTouchStart = (e: TouchEvent) => {\n // Check if touching a draggable element (node, waypoint, pipe segment)\n const target = e.target as Element;\n const isDraggableNode = target.closest('[data-node-id]');\n const isWaypoint = target.classList.contains('pipe-route-point');\n const isPipeSegment = target.classList.contains('pipe-segment-add') || target.classList.contains('pipe-segment-add-area');\n \n if ((isDraggableNode || isWaypoint || isPipeSegment) && e.touches.length === 1) {\n return; // Let drag handler take over for single touch\n }\n\n if (e.touches.length === 1 && enablePan) {\n // Single touch - pan\n isPanningRef.current = true;\n lastMousePosRef.current = {\n x: e.touches[0].clientX,\n y: e.touches[0].clientY,\n };\n } else if (e.touches.length === 2 && enableZoom) {\n // Two touches - prepare for pinch zoom\n const touch1 = e.touches[0];\n const touch2 = e.touches[1];\n const distance = Math.hypot(\n touch2.clientX - touch1.clientX,\n touch2.clientY - touch1.clientY\n );\n lastTouchDistanceRef.current = distance;\n }\n };\n\n const handleTouchMove = (e: TouchEvent) => {\n e.preventDefault();\n\n if (e.touches.length === 1 && isPanningRef.current && enablePan) {\n // Single touch pan\n const touch = e.touches[0];\n const dx = touch.clientX - lastMousePosRef.current.x;\n const dy = touch.clientY - lastMousePosRef.current.y;\n lastMousePosRef.current = { x: touch.clientX, y: touch.clientY };\n\n setViewBox((prev) => {\n const rect = svg.getBoundingClientRect();\n const scaleX = prev.width / rect.width;\n const scaleY = prev.height / rect.height;\n\n return {\n ...prev,\n x: prev.x - dx * scaleX,\n y: prev.y - dy * scaleY,\n };\n });\n } else if (e.touches.length === 2 && enableZoom && lastTouchDistanceRef.current) {\n // Pinch zoom\n const touch1 = e.touches[0];\n const touch2 = e.touches[1];\n const distance = Math.hypot(\n touch2.clientX - touch1.clientX,\n touch2.clientY - touch1.clientY\n );\n\n const scale = distance / lastTouchDistanceRef.current;\n lastTouchDistanceRef.current = distance;\n\n // Get center point between two touches\n const centerX = (touch1.clientX + touch2.clientX) / 2;\n const centerY = (touch1.clientY + touch2.clientY) / 2;\n const rect = svg.getBoundingClientRect();\n\n setViewBox((prev) => {\n // Convert touch center to viewBox coordinates\n const svgX = prev.x + ((centerX - rect.left) / rect.width) * prev.width;\n const svgY = prev.y + ((centerY - rect.top) / rect.height) * prev.height;\n\n // Calculate new dimensions\n let newWidth = prev.width / scale;\n let newHeight = prev.height / scale;\n\n // Clamp zoom level\n const currentZoom = initialViewBox.width / prev.width;\n const newZoom = currentZoom * scale;\n if (newZoom < minZoom || newZoom > 1 / minZoom) {\n return prev;\n }\n\n // Adjust position to zoom toward touch center\n const newX = svgX - ((centerX - rect.left) / rect.width) * newWidth;\n const newY = svgY - ((centerY - rect.top) / rect.height) * newHeight;\n\n return {\n x: newX,\n y: newY,\n width: newWidth,\n height: newHeight,\n };\n });\n }\n };\n\n const handleTouchEnd = () => {\n isPanningRef.current = false;\n lastTouchDistanceRef.current = null;\n };\n\n svg.addEventListener('touchstart', handleTouchStart);\n svg.addEventListener('touchmove', handleTouchMove, { passive: false });\n svg.addEventListener('touchend', handleTouchEnd);\n\n return () => {\n svg.removeEventListener('touchstart', handleTouchStart);\n svg.removeEventListener('touchmove', handleTouchMove);\n svg.removeEventListener('touchend', handleTouchEnd);\n };\n }, [enablePan, enableZoom, minZoom, initialViewBox]);\n\n const screenToWorld = useCallback(\n (screenX: number, screenY: number): { x: number; y: number } => {\n const svg = svgRef.current;\n if (!svg) return { x: screenX, y: screenY };\n\n const rect = svg.getBoundingClientRect();\n \n // Account for preserveAspectRatio=\"xMidYMid meet\"\n const svgAspect = rect.width / rect.height;\n const viewBoxAspect = viewBox.width / viewBox.height;\n \n let actualWidth = rect.width;\n let actualHeight = rect.height;\n let offsetX = 0;\n let offsetY = 0;\n \n if (svgAspect > viewBoxAspect) {\n // SVG is wider - pillarboxing on sides\n actualWidth = rect.height * viewBoxAspect;\n offsetX = (rect.width - actualWidth) / 2;\n } else {\n // SVG is taller - letterboxing on top/bottom\n actualHeight = rect.width / viewBoxAspect;\n offsetY = (rect.height - actualHeight) / 2;\n }\n \n const relativeX = (screenX - rect.left - offsetX) / actualWidth;\n const relativeY = (screenY - rect.top - offsetY) / actualHeight;\n\n const worldX = viewBox.x + relativeX * viewBox.width;\n const worldY = viewBox.y + relativeY * viewBox.height;\n\n return { x: worldX, y: worldY };\n },\n [viewBox]\n );\n\n return {\n viewBox,\n resetViewBox,\n zoomIn,\n zoomOut,\n fitToContent,\n setViewBox,\n screenToWorld,\n svgRef,\n };\n}\n","import { useState, useCallback } from 'react';\n\nexport interface SelectionState {\n /** IDs of selected nodes */\n selectedNodeIds: Set<string>;\n /** IDs of selected pipes */\n selectedPipeIds: Set<string>;\n}\n\nexport interface UseSelectionOptions {\n /** Allow multiple selection with Ctrl/Cmd key (default: true) */\n multiSelect?: boolean;\n /** Initial selection state */\n initialSelection?: SelectionState;\n /** Callback when selection changes */\n onSelectionChange?: (selection: SelectionState) => void;\n}\n\nexport interface UseSelectionResult {\n /** Current selection state */\n selection: SelectionState;\n /** Check if a node is selected */\n isNodeSelected: (nodeId: string) => boolean;\n /** Check if a pipe is selected */\n isPipeSelected: (pipeId: string) => boolean;\n /** Select a node */\n selectNode: (nodeId: string, multiSelect?: boolean) => void;\n /** Select a pipe */\n selectPipe: (pipeId: string, multiSelect?: boolean) => void;\n /** Clear all selections */\n clearSelection: () => void;\n /** Select multiple nodes */\n selectNodes: (nodeIds: string[]) => void;\n /** Select multiple pipes */\n selectPipes: (pipeIds: string[]) => void;\n}\n\n/**\n * Custom hook for managing selection state in P&ID diagrams\n * \n * Supports:\n * - Single selection (click)\n * - Multi-selection (Ctrl+click)\n * - Programmatic selection\n * - Selection callbacks\n */\nexport function useSelection(options: UseSelectionOptions = {}): UseSelectionResult {\n const {\n multiSelect = true,\n initialSelection = { selectedNodeIds: new Set(), selectedPipeIds: new Set() },\n onSelectionChange,\n } = options;\n\n const [selection, setSelection] = useState<SelectionState>(initialSelection);\n\n const notifyChange = useCallback(\n (newSelection: SelectionState) => {\n if (onSelectionChange) {\n onSelectionChange(newSelection);\n }\n },\n [onSelectionChange]\n );\n\n const isNodeSelected = useCallback(\n (nodeId: string) => selection.selectedNodeIds.has(nodeId),\n [selection.selectedNodeIds]\n );\n\n const isPipeSelected = useCallback(\n (pipeId: string) => selection.selectedPipeIds.has(pipeId),\n [selection.selectedPipeIds]\n );\n\n const selectNode = useCallback(\n (nodeId: string, isMultiSelect = false) => {\n setSelection((prev) => {\n const newNodeIds = new Set<string>(isMultiSelect && multiSelect ? prev.selectedNodeIds : []);\n \n if (newNodeIds.has(nodeId)) {\n // Deselect if already selected in multi-select mode\n newNodeIds.delete(nodeId);\n } else {\n newNodeIds.add(nodeId);\n }\n\n const newSelection: SelectionState = {\n selectedNodeIds: newNodeIds,\n selectedPipeIds: isMultiSelect && multiSelect ? prev.selectedPipeIds : new Set<string>(),\n };\n\n notifyChange(newSelection);\n return newSelection;\n });\n },\n [multiSelect, notifyChange]\n );\n\n const selectPipe = useCallback(\n (pipeId: string, isMultiSelect = false) => {\n setSelection((prev) => {\n const newPipeIds = new Set<string>(isMultiSelect && multiSelect ? prev.selectedPipeIds : []);\n \n if (newPipeIds.has(pipeId)) {\n // Deselect if already selected in multi-select mode\n newPipeIds.delete(pipeId);\n } else {\n newPipeIds.add(pipeId);\n }\n\n const newSelection: SelectionState = {\n selectedNodeIds: isMultiSelect && multiSelect ? prev.selectedNodeIds : new Set<string>(),\n selectedPipeIds: newPipeIds,\n };\n\n notifyChange(newSelection);\n return newSelection;\n });\n },\n [multiSelect, notifyChange]\n );\n\n const clearSelection = useCallback(() => {\n const newSelection = {\n selectedNodeIds: new Set<string>(),\n selectedPipeIds: new Set<string>(),\n };\n setSelection(newSelection);\n notifyChange(newSelection);\n }, [notifyChange]);\n\n const selectNodes = useCallback(\n (nodeIds: string[]) => {\n const newSelection = {\n selectedNodeIds: new Set(nodeIds),\n selectedPipeIds: new Set<string>(),\n };\n setSelection(newSelection);\n notifyChange(newSelection);\n },\n [notifyChange]\n );\n\n const selectPipes = useCallback(\n (pipeIds: string[]) => {\n const newSelection = {\n selectedNodeIds: new Set<string>(),\n selectedPipeIds: new Set(pipeIds),\n };\n setSelection(newSelection);\n notifyChange(newSelection);\n },\n [notifyChange]\n );\n\n return {\n selection,\n isNodeSelected,\n isPipeSelected,\n selectNode,\n selectPipe,\n clearSelection,\n selectNodes,\n selectPipes,\n };\n}\n","import { useState, useCallback, useEffect, useRef } from 'react';\nimport type { TelemetryBinding, TelemetryMap, TelemetryValue } from '../schema/TelemetryBinding';\n\n// Re-export types for consumer convenience\nexport type { TelemetryBinding, TelemetryMap, TelemetryValue };\n\nexport interface UseTelemetryOptions {\n /** Initial telemetry bindings */\n initialBindings?: TelemetryBinding[];\n /** Callback when telemetry values change */\n onTelemetryChange?: (bindings: TelemetryMap) => void;\n /** Auto-refresh interval in milliseconds (0 = disabled) */\n refreshInterval?: number;\n}\n\nexport interface UseTelemetryResult {\n /** Current telemetry bindings map */\n telemetry: TelemetryMap;\n /** Update a single telemetry value */\n updateTelemetry: (nodeId: string, value: TelemetryValue) => void;\n /** Update multiple telemetry values at once */\n updateTelemetryBatch: (updates: Array<{ nodeId: string; value: TelemetryValue; history?: number[] }>) => void;\n /** Get telemetry for a specific node */\n getTelemetry: (nodeId: string) => TelemetryBinding | undefined;\n /** Clear all telemetry data */\n clearTelemetry: () => void;\n /** Set telemetry bindings */\n setBindings: (bindings: TelemetryBinding[]) => void;\n}\n\n/**\n * Custom hook for managing telemetry data bindings\n * \n * Supports:\n * - Real-time value updates\n * - Status monitoring (normal, warning, alarm, fault, off)\n * - Batch updates for performance\n * - Auto-refresh capability\n * - Quality indicators\n */\nexport function useTelemetry(options: UseTelemetryOptions = {}): UseTelemetryResult {\n const {\n initialBindings = [],\n onTelemetryChange,\n refreshInterval = 0,\n } = options;\n\n // Initialize telemetry map from bindings\n const [telemetry, setTelemetry] = useState<TelemetryMap>(() => {\n const map = new Map<string, TelemetryBinding>();\n initialBindings.forEach((binding) => {\n map.set(binding.nodeId, binding);\n });\n return map;\n });\n\n const telemetryRef = useRef(telemetry);\n telemetryRef.current = telemetry;\n\n const notifyChange = useCallback(\n (newTelemetry: TelemetryMap) => {\n if (onTelemetryChange) {\n onTelemetryChange(newTelemetry);\n }\n },\n [onTelemetryChange]\n );\n\n const updateTelemetry = useCallback(\n (nodeId: string, value: TelemetryValue) => {\n setTelemetry((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n \n if (existing) {\n // Update existing binding\n newMap.set(nodeId, {\n ...existing,\n value: {\n ...value,\n timestamp: value.timestamp ?? Date.now(),\n },\n });\n } else {\n // Create new binding\n newMap.set(nodeId, {\n nodeId,\n tag: nodeId, // Default tag to nodeId\n value: {\n ...value,\n timestamp: value.timestamp ?? Date.now(),\n },\n });\n }\n\n notifyChange(newMap);\n return newMap;\n });\n },\n [notifyChange]\n );\n\n const updateTelemetryBatch = useCallback(\n (updates: Array<{ \n nodeId: string; \n tag?: string; \n value?: TelemetryValue; \n history?: number[]; \n display?: any; \n thresholds?: any; \n statusColors?: any;\n remove?: boolean \n }>) => {\n setTelemetry((prev) => {\n const newMap = new Map(prev);\n let hasChanges = false;\n\n updates.forEach(({ nodeId, tag, value, history, display, thresholds, statusColors, remove }) => {\n if (remove) {\n // Remove telemetry binding\n if (newMap.has(nodeId)) {\n newMap.delete(nodeId);\n hasChanges = true;\n }\n return;\n }\n\n const existing = newMap.get(nodeId);\n\n if (existing) {\n newMap.set(nodeId, {\n ...existing,\n ...(tag !== undefined && { tag }),\n ...(value !== undefined && {\n value: {\n ...value,\n timestamp: value.timestamp ?? Date.now(),\n },\n }),\n ...(history !== undefined && { history }),\n ...(display !== undefined && { display }),\n ...(thresholds !== undefined && { thresholds }),\n ...(statusColors !== undefined && { statusColors }),\n });\n hasChanges = true;\n } else if (value) {\n newMap.set(nodeId, {\n nodeId,\n tag: tag ?? nodeId,\n value: {\n ...value,\n timestamp: value.timestamp ?? Date.now(),\n },\n ...(history !== undefined && { history }),\n ...(display !== undefined && { display }),\n ...(thresholds !== undefined && { thresholds }),\n ...(statusColors !== undefined && { statusColors }),\n });\n hasChanges = true;\n }\n });\n\n if (hasChanges) {\n notifyChange(newMap);\n }\n return newMap;\n });\n },\n [notifyChange]\n );\n\n const getTelemetry = useCallback(\n (nodeId: string) => telemetryRef.current.get(nodeId),\n []\n );\n\n const clearTelemetry = useCallback(() => {\n const newMap = new Map<string, TelemetryBinding>();\n setTelemetry(newMap);\n notifyChange(newMap);\n }, [notifyChange]);\n\n const setBindings = useCallback(\n (bindings: TelemetryBinding[]) => {\n const newMap = new Map<string, TelemetryBinding>();\n bindings.forEach((binding) => {\n newMap.set(binding.nodeId, binding);\n });\n setTelemetry(newMap);\n notifyChange(newMap);\n },\n [notifyChange]\n );\n\n // Auto-refresh functionality\n useEffect(() => {\n if (refreshInterval <= 0) return;\n\n const intervalId = setInterval(() => {\n // Trigger change notification to allow external refresh logic\n notifyChange(telemetryRef.current);\n }, refreshInterval);\n\n return () => clearInterval(intervalId);\n }, [refreshInterval, notifyChange]);\n\n return {\n telemetry,\n updateTelemetry,\n updateTelemetryBatch,\n getTelemetry,\n clearTelemetry,\n setBindings,\n };\n}\n","import { useEffect } from 'react';\nimport type { TelemetryMap } from './useTelemetry';\nimport type { Diagram } from '../schema';\nimport type { TelemetryStatus } from '../schema/TelemetryBinding';\n\n/**\n * Options for syncing telemetry status to node status\n */\nexport interface UseTelemetryStatusOptions {\n /** Whether status sync is enabled (default: true) */\n enabled?: boolean;\n /**\n * Custom status mapping function.\n *\n * A custom mapper replaces the default entirely. It must handle every\n * member of `TelemetryStatus`, including `unknown`. A mapper that returns\n * a healthy-looking status for `unknown` reintroduces the defect the\n * default mapping closes.\n */\n mapStatus?: (telemetryStatus: TelemetryStatus) => string;\n}\n\n/**\n * Hook to sync telemetry status to node instance status for dynamic coloring\n * \n * Updates node.status based on telemetry status values:\n * - 'normal' → 'running'\n * - 'warning' → 'warning'\n * - 'alarm' / 'fault' → 'alarm'\n * - 'off' → 'stopped'\n * - 'unknown' → 'unknown' (untrusted read; must not read as stopped)\n * \n * @param telemetry - Telemetry map from useTelemetry\n * @param diagram - Current diagram state\n * @param setDiagram - Diagram state setter\n * @param options - Configuration options\n * \n * @example\n * ```tsx\n * const { telemetry } = useTelemetry();\n * const [diagram, setDiagram] = useState<Diagram>({...});\n * \n * useTelemetryStatus(telemetry, diagram, setDiagram, {\n * enabled: mode === 'runtime'\n * });\n * ```\n */\nexport function useTelemetryStatus(\n telemetry: TelemetryMap,\n diagram: Diagram,\n setDiagram: (diagram: Diagram) => void,\n options: UseTelemetryStatusOptions = {}\n) {\n const { enabled = true, mapStatus } = options;\n\n useEffect(() => {\n if (!enabled) return;\n\n // Map telemetry status to node status\n const defaultMapStatus = (telemetryStatus: TelemetryStatus): string => {\n switch (telemetryStatus) {\n case 'normal':\n return 'running';\n case 'warning':\n return 'warning';\n case 'alarm':\n case 'fault':\n return 'alarm';\n case 'off':\n return 'stopped';\n case 'unknown':\n // An untrusted read is not a confirmed-off device. Keep it distinct\n // so the symbol does not paint with the OFF colours.\n return 'unknown';\n default: {\n // Compile-time guard: this assignment fails to build if a new\n // TelemetryStatus member has no case above. Without it, a future\n // status would silently paint as 'stopped', which is the defect\n // the 'unknown' case exists to close.\n const exhaustive: never = telemetryStatus;\n void exhaustive;\n // Runtime last resort for a value that arrived from outside the\n // type system.\n return 'stopped';\n }\n }\n };\n\n const statusMapper = mapStatus || defaultMapStatus;\n\n // Check if any node status needs updating\n let needsUpdate = false;\n const updatedNodes = diagram.nodes.map((node) => {\n const binding = telemetry.get(node.id);\n if (!binding) return node;\n\n const newStatus = statusMapper(binding.value.status);\n if (node.status !== newStatus) {\n needsUpdate = true;\n return { ...node, status: newStatus };\n }\n return node;\n });\n\n if (needsUpdate) {\n setDiagram({\n ...diagram,\n nodes: updatedNodes,\n });\n }\n }, [telemetry, diagram, setDiagram, enabled, mapStatus]);\n}\n","import { useEffect } from 'react';\nimport type { TelemetryMap } from './useTelemetry';\nimport type { Diagram } from '../schema';\nimport { resolvePresetColor } from '../symbols/appearanceSchema';\n\n/** Options for the signal-binding sync. */\nexport interface UseBooleanIndicatorsOptions {\n /** Whether the sync is enabled (default: true). Disable in edit mode. */\n enabled?: boolean;\n /** Symbol id treated as a status indicator (default: 'status-indicator'). */\n symbolId?: string;\n}\n\n/**\n * Drive a status-indicator node's color and visibility from independent named\n * signals.\n *\n * Each indicator node names two tags in its metadata:\n * - `colorSource` — a tag whose truthy value shows `onColor`, falsy shows `offColor`.\n * - `visibleSource` — a tag whose truthy value shows the node, falsy hides it.\n *\n * Both are resolved against a tag → value index built from every published\n * telemetry binding, so color and visibility can follow completely different\n * signals. A blank source leaves that property alone (color unbound, visibility\n * static). Writes `customColors.fill` and `visible` — never touches the renderer\n * props, so the NodeRenderer memo boundary stays intact.\n *\n * @example\n * ```tsx\n * useBooleanIndicators(telemetry, diagram, setDiagram, { enabled: mode === 'runtime' });\n * ```\n */\nexport function useBooleanIndicators(\n telemetry: TelemetryMap,\n diagram: Diagram,\n setDiagram: (diagram: Diagram) => void,\n options: UseBooleanIndicatorsOptions = {}\n) {\n const { enabled = true, symbolId = 'status-indicator' } = options;\n\n useEffect(() => {\n if (!enabled) return;\n\n // Index every published signal by its tag, so any node can resolve any tag.\n const tagIndex = new Map<string, number | string | boolean>();\n telemetry.forEach((binding) => {\n if (binding.tag) tagIndex.set(binding.tag, binding.value.value);\n });\n\n let needsUpdate = false;\n const updatedNodes = diagram.nodes.map((node) => {\n if (node.symbolId !== symbolId) return node;\n\n const meta = node.metadata ?? {};\n const colorSource = typeof meta.colorSource === 'string' ? meta.colorSource : '';\n const visibleSource = typeof meta.visibleSource === 'string' ? meta.visibleSource : '';\n\n let next = node;\n\n if (colorSource) {\n const isTrue = Boolean(tagIndex.get(colorSource));\n const fill = resolvePresetColor(\n String(isTrue ? meta.onColor ?? 'green' : meta.offColor ?? 'red')\n );\n if (node.customColors?.fill !== fill) {\n next = { ...next, customColors: { ...next.customColors, fill } };\n }\n }\n\n if (visibleSource) {\n const vis = Boolean(tagIndex.get(visibleSource));\n if ((next.visible ?? true) !== vis) {\n next = { ...next, visible: vis };\n }\n }\n\n if (next !== node) needsUpdate = true;\n return next;\n });\n\n if (needsUpdate) {\n setDiagram({ ...diagram, nodes: updatedNodes });\n }\n }, [telemetry, diagram, setDiagram, enabled, symbolId]);\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\n\nexport interface DragPoint {\n x: number;\n y: number;\n}\n\nexport interface DragState {\n /** Whether drag is currently active */\n isDragging: boolean;\n /** Initial position when drag started */\n startPosition: DragPoint | null;\n /** Current drag offset from start */\n offset: DragPoint;\n /** ID of the item being dragged */\n draggedId: string | null;\n}\n\nexport interface UseDragOptions {\n /** Callback when drag starts */\n onDragStart?: (id: string, position: DragPoint) => void;\n /** Callback during drag movement */\n onDragMove?: (id: string, offset: DragPoint, position: DragPoint) => void;\n /** Callback when drag ends */\n onDragEnd?: (id: string, offset: DragPoint, finalPosition: DragPoint) => void;\n /** Snap to grid size (0 = no snap) */\n snapToGrid?: number;\n /** Minimum drag distance before triggering (prevents accidental drags) */\n dragThreshold?: number;\n /** Convert screen coordinates to world coordinates */\n screenToWorld?: (screenX: number, screenY: number) => DragPoint;\n}\n\nexport interface UseDragResult {\n /** Current drag state */\n dragState: DragState;\n /** Start dragging an item */\n startDrag: (id: string, event: React.MouseEvent | React.TouchEvent, itemPosition: DragPoint) => void;\n /** Cancel current drag */\n cancelDrag: () => void;\n /** Check if a specific item is being dragged */\n isDraggingItem: (id: string) => boolean;\n}\n\n/**\n * Custom hook for drag-and-drop functionality in P&ID diagrams\n * \n * Supports:\n * - Mouse drag\n * - Touch drag\n * - Snap-to-grid\n * - World coordinate conversion (for SVG viewBox)\n * - Drag threshold to prevent accidental drags\n */\nexport function useDrag(options: UseDragOptions = {}): UseDragResult {\n const {\n onDragStart,\n onDragMove,\n onDragEnd,\n snapToGrid = 0,\n dragThreshold = 3,\n screenToWorld = (x, y) => ({ x, y }),\n } = options;\n\n const [dragState, setDragState] = useState<DragState>({\n isDragging: false,\n startPosition: null,\n offset: { x: 0, y: 0 },\n draggedId: null,\n });\n\n const [isListening, setIsListening] = useState(false);\n const dragStartRef = useRef<{ id: string; screenStart: DragPoint; worldStart: DragPoint; itemPosition: DragPoint; clickOffset: DragPoint } | null>(null);\n const hasMovedRef = useRef(false);\n\n const applySnap = useCallback(\n (value: number): number => {\n if (snapToGrid <= 0) return value;\n return Math.round(value / snapToGrid) * snapToGrid;\n },\n [snapToGrid]\n );\n\n const handleMouseMove = useCallback(\n (event: MouseEvent) => {\n if (!dragStartRef.current) return;\n\n const { id, screenStart: _screenStart, itemPosition, clickOffset } = dragStartRef.current;\n const currentWorld = screenToWorld(event.clientX, event.clientY);\n\n // New position = current cursor - click offset\n const newX = currentWorld.x - clickOffset.x;\n const newY = currentWorld.y - clickOffset.y;\n\n const rawOffset = {\n x: newX - itemPosition.x,\n y: newY - itemPosition.y,\n };\n\n // Check if we've exceeded the drag threshold\n const distance = Math.sqrt(rawOffset.x ** 2 + rawOffset.y ** 2);\n if (!hasMovedRef.current && distance < dragThreshold) {\n return;\n }\n\n if (!hasMovedRef.current) {\n hasMovedRef.current = true;\n setDragState({\n isDragging: true,\n startPosition: itemPosition,\n offset: { x: 0, y: 0 },\n draggedId: id,\n });\n onDragStart?.(id, itemPosition);\n }\n\n const snappedOffset = {\n x: applySnap(rawOffset.x),\n y: applySnap(rawOffset.y),\n };\n\n const newPosition = {\n x: applySnap(itemPosition.x + rawOffset.x),\n y: applySnap(itemPosition.y + rawOffset.y),\n };\n\n setDragState((prev) => ({\n ...prev,\n offset: snappedOffset,\n }));\n\n onDragMove?.(id, snappedOffset, newPosition);\n },\n [screenToWorld, dragThreshold, applySnap, onDragStart, onDragMove]\n );\n\n const handleMouseUp = useCallback(\n (event: MouseEvent) => {\n if (!dragStartRef.current || !hasMovedRef.current) {\n dragStartRef.current = null;\n hasMovedRef.current = false;\n setIsListening(false);\n return;\n }\n\n const { id, itemPosition, clickOffset } = dragStartRef.current;\n const currentWorld = screenToWorld(event.clientX, event.clientY);\n\n const newX = currentWorld.x - clickOffset.x;\n const newY = currentWorld.y - clickOffset.y;\n\n const rawOffset = {\n x: newX - itemPosition.x,\n y: newY - itemPosition.y,\n };\n\n const snappedOffset = {\n x: applySnap(rawOffset.x),\n y: applySnap(rawOffset.y),\n };\n\n const finalPosition = {\n x: applySnap(itemPosition.x + rawOffset.x),\n y: applySnap(itemPosition.y + rawOffset.y),\n };\n\n onDragEnd?.(id, snappedOffset, finalPosition);\n\n setDragState({\n isDragging: false,\n startPosition: null,\n offset: { x: 0, y: 0 },\n draggedId: null,\n });\n\n dragStartRef.current = null;\n hasMovedRef.current = false;\n setIsListening(false);\n },\n [screenToWorld, applySnap, onDragEnd]\n );\n\n const handleTouchMove = useCallback(\n (event: TouchEvent) => {\n if (!dragStartRef.current || event.touches.length !== 1) return;\n\n const touch = event.touches[0];\n const { id, screenStart: _screenStart2, itemPosition, clickOffset } = dragStartRef.current;\n const currentWorld = screenToWorld(touch.clientX, touch.clientY);\n\n const newX = currentWorld.x - clickOffset.x;\n const newY = currentWorld.y - clickOffset.y;\n\n const rawOffset = {\n x: newX - itemPosition.x,\n y: newY - itemPosition.y,\n };\n\n const distance = Math.sqrt(rawOffset.x ** 2 + rawOffset.y ** 2);\n if (!hasMovedRef.current && distance < dragThreshold) {\n return;\n }\n\n event.preventDefault(); // Prevent scrolling\n\n if (!hasMovedRef.current) {\n hasMovedRef.current = true;\n setDragState({\n isDragging: true,\n startPosition: itemPosition,\n offset: { x: 0, y: 0 },\n draggedId: id,\n });\n onDragStart?.(id, itemPosition);\n }\n\n const snappedOffset = {\n x: applySnap(rawOffset.x),\n y: applySnap(rawOffset.y),\n };\n\n const newPosition = {\n x: applySnap(itemPosition.x + rawOffset.x),\n y: applySnap(itemPosition.y + rawOffset.y),\n };\n\n setDragState((prev) => ({\n ...prev,\n offset: snappedOffset,\n }));\n\n onDragMove?.(id, snappedOffset, newPosition);\n },\n [screenToWorld, dragThreshold, applySnap, onDragStart, onDragMove]\n );\n\n const handleTouchEnd = useCallback(\n (event: TouchEvent) => {\n if (!dragStartRef.current || !hasMovedRef.current) {\n dragStartRef.current = null;\n hasMovedRef.current = false;\n setIsListening(false);\n return;\n }\n\n const { id, itemPosition, clickOffset } = dragStartRef.current;\n const touch = event.changedTouches[0];\n const currentWorld = screenToWorld(touch.clientX, touch.clientY);\n\n const newX = currentWorld.x - clickOffset.x;\n const newY = currentWorld.y - clickOffset.y;\n\n const rawOffset = {\n x: newX - itemPosition.x,\n y: newY - itemPosition.y,\n };\n\n const snappedOffset = {\n x: applySnap(rawOffset.x),\n y: applySnap(rawOffset.y),\n };\n\n const finalPosition = {\n x: applySnap(itemPosition.x + rawOffset.x),\n y: applySnap(itemPosition.y + rawOffset.y),\n };\n\n onDragEnd?.(id, snappedOffset, finalPosition);\n\n setDragState({\n isDragging: false,\n startPosition: null,\n offset: { x: 0, y: 0 },\n draggedId: null,\n });\n\n dragStartRef.current = null;\n hasMovedRef.current = false;\n setIsListening(false);\n },\n [screenToWorld, applySnap, onDragEnd]\n );\n\n useEffect(() => {\n // Only attach listeners when actively listening for drag events\n if (!isListening) return;\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n document.addEventListener('touchmove', handleTouchMove, { passive: false });\n document.addEventListener('touchend', handleTouchEnd);\n document.addEventListener('touchcancel', handleTouchEnd);\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n document.removeEventListener('touchmove', handleTouchMove);\n document.removeEventListener('touchend', handleTouchEnd);\n document.removeEventListener('touchcancel', handleTouchEnd);\n };\n }, [isListening, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]);\n\n const startDrag = useCallback(\n (id: string, event: React.MouseEvent | React.TouchEvent, itemPosition: DragPoint) => {\n event.stopPropagation();\n\n let clientX: number;\n let clientY: number;\n\n if ('touches' in event) {\n if (event.touches.length !== 1) return;\n clientX = event.touches[0].clientX;\n clientY = event.touches[0].clientY;\n } else {\n clientX = event.clientX;\n clientY = event.clientY;\n }\n\n const worldStart = screenToWorld(clientX, clientY);\n \n // Calculate offset from click position to item position\n const clickOffset = {\n x: worldStart.x - itemPosition.x,\n y: worldStart.y - itemPosition.y,\n };\n\n dragStartRef.current = {\n id,\n screenStart: { x: clientX, y: clientY },\n worldStart,\n itemPosition,\n clickOffset,\n };\n hasMovedRef.current = false;\n setIsListening(true); // Start listening for move/up events\n },\n [screenToWorld]\n );\n\n const cancelDrag = useCallback(() => {\n dragStartRef.current = null;\n hasMovedRef.current = false;\n setIsListening(false);\n setDragState({\n isDragging: false,\n startPosition: null,\n offset: { x: 0, y: 0 },\n draggedId: null,\n });\n }, []);\n\n const isDraggingItem = useCallback(\n (id: string) => dragState.isDragging && dragState.draggedId === id,\n [dragState.isDragging, dragState.draggedId]\n );\n\n return {\n dragState,\n startDrag,\n cancelDrag,\n isDraggingItem,\n };\n}\n","import { useEffect } from 'react';\n\nexport interface KeyboardShortcutsOptions {\n /** Callback when Delete or Backspace is pressed */\n onDelete?: () => void;\n /** Callback when Ctrl+C is pressed */\n onCopy?: () => void;\n /** Callback when Ctrl+V is pressed */\n onPaste?: () => void;\n /** Callback when Ctrl+Z is pressed (undo) */\n onUndo?: () => void;\n /** Callback when Ctrl+Y or Ctrl+Shift+Z is pressed (redo) */\n onRedo?: () => void;\n /** Callback when Ctrl+A is pressed (select all) */\n onSelectAll?: () => void;\n /** Enable keyboard shortcuts (default: true) */\n enabled?: boolean;\n}\n\n/**\n * Custom hook for handling keyboard shortcuts in P&ID diagrams\n * \n * Supported shortcuts:\n * - Delete/Backspace: Delete selected items\n * - Ctrl+C: Copy selected items\n * - Ctrl+V: Paste copied items\n * - Ctrl+Z: Undo last action\n * - Ctrl+Y / Ctrl+Shift+Z: Redo\n * - Ctrl+A: Select all\n */\nexport function useKeyboardShortcuts(options: KeyboardShortcutsOptions): void {\n const {\n onDelete,\n onCopy,\n onPaste,\n onUndo,\n onRedo,\n onSelectAll,\n enabled = true,\n } = options;\n\n useEffect(() => {\n if (!enabled) return;\n\n const handleKeyDown = (event: KeyboardEvent) => {\n // Ignore if user is typing in an input field\n const target = event.target as HTMLElement;\n if (\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.isContentEditable\n ) {\n return;\n }\n\n const isCtrl = event.ctrlKey || event.metaKey;\n const isShift = event.shiftKey;\n\n // Delete or Backspace\n if ((event.key === 'Delete' || event.key === 'Backspace') && !isCtrl) {\n event.preventDefault();\n onDelete?.();\n return;\n }\n\n // Ctrl+C - Copy\n if (isCtrl && event.key === 'c') {\n event.preventDefault();\n onCopy?.();\n return;\n }\n\n // Ctrl+V - Paste\n if (isCtrl && event.key === 'v') {\n event.preventDefault();\n onPaste?.();\n return;\n }\n\n // Ctrl+Z - Undo\n if (isCtrl && event.key === 'z' && !isShift) {\n event.preventDefault();\n onUndo?.();\n return;\n }\n\n // Ctrl+Y or Ctrl+Shift+Z - Redo\n if (isCtrl && (event.key === 'y' || (event.key === 'z' && isShift))) {\n event.preventDefault();\n onRedo?.();\n return;\n }\n\n // Ctrl+A - Select all\n if (isCtrl && event.key === 'a') {\n event.preventDefault();\n onSelectAll?.();\n return;\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [enabled, onDelete, onCopy, onPaste, onUndo, onRedo, onSelectAll]);\n}\n","import type { NodeInstance, Point } from '../schema';\nimport { getSymbolDefinition } from '../symbols';\n\n/**\n * Calculate the world position of a port on a node instance\n * \n * Takes into account the node's transform (position, rotation, scale, flip)\n * and the port's position within the symbol's viewBox\n */\nexport function getPortWorldPosition(\n node: NodeInstance,\n portId: string\n): Point | null {\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol) return null;\n\n const port = symbol.ports.find((p) => p.id === portId);\n if (!port) return null;\n\n const { x: nodeX, y: nodeY, rotation = 0, scale = 1, flipX = false, flipY = false } = node.transform;\n\n // Start with port position relative to symbol\n let portX = port.x;\n let portY = port.y;\n\n // Apply flip transforms (relative to symbol center)\n const centerX = symbol.viewBox.width / 2;\n const centerY = symbol.viewBox.height / 2;\n \n if (flipX) {\n portX = symbol.viewBox.width - portX;\n }\n if (flipY) {\n portY = symbol.viewBox.height - portY;\n }\n\n // Apply scale (relative to symbol center)\n portX = centerX + (portX - centerX) * scale;\n portY = centerY + (portY - centerY) * scale;\n\n // Apply rotation (relative to symbol center)\n if (rotation !== 0) {\n const rad = (rotation * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n \n const dx = portX - centerX;\n const dy = portY - centerY;\n \n portX = centerX + dx * cos - dy * sin;\n portY = centerY + dx * sin + dy * cos;\n }\n\n // Translate to world position\n return {\n x: nodeX + portX,\n y: nodeY + portY,\n };\n}\n\n/**\n * Generate route points for a pipe from source port to destination port\n * \n * Options:\n * - direct: Straight line from source to destination\n * - orthogonal: Right-angle routing (future enhancement)\n * - custom: Provide your own waypoints\n */\nexport function generateRoutePoints(\n fromNode: NodeInstance,\n fromPortId: string,\n toNode: NodeInstance,\n toPortId: string,\n options?: {\n style?: 'direct' | 'orthogonal';\n waypoints?: Point[];\n }\n): Point[] {\n const startPoint = getPortWorldPosition(fromNode, fromPortId);\n const endPoint = getPortWorldPosition(toNode, toPortId);\n\n if (!startPoint || !endPoint) {\n return [];\n }\n\n const style = options?.style || 'direct';\n\n if (style === 'direct') {\n // Straight line from start to end\n return [startPoint, endPoint];\n }\n\n if (style === 'orthogonal' && options?.waypoints) {\n // Start → waypoints → end\n return [startPoint, ...options.waypoints, endPoint];\n }\n\n if (style === 'orthogonal') {\n // Simple orthogonal routing: horizontal then vertical\n const midX = (startPoint.x + endPoint.x) / 2;\n return [\n startPoint,\n { x: midX, y: startPoint.y },\n { x: midX, y: endPoint.y },\n endPoint,\n ];\n }\n\n return [startPoint, endPoint];\n}\n\n/**\n * Validate that a node has a specific port\n */\nexport function nodeHasPort(node: NodeInstance, portId: string): boolean {\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol) return false;\n return symbol.ports.some((p) => p.id === portId);\n}\n","import React, { useState } from 'react';\nimport { getAvailableSymbols, getSymbolDefinition } from '../../diagram/symbols';\nimport type { SymbolDefinition } from '../../diagram/schema';\n\nexport interface SymbolLibraryProps {\n /** Optional className for styling */\n className?: string;\n /** Callback when a symbol drag starts */\n onSymbolDragStart?: (symbolId: string, event: React.DragEvent) => void;\n /** Show category filter */\n showCategories?: boolean;\n /** Custom symbol categories */\n categories?: string[];\n}\n\n/**\n * SymbolLibrary - Browse and drag symbols onto the canvas\n *\n * Displays available symbols organized by category\n * Supports drag-and-drop to add new nodes to diagrams\n */\nexport function SymbolLibrary({\n className = '',\n onSymbolDragStart,\n showCategories = true,\n categories = ['Valves', 'Pumps', 'Tanks', 'Instruments', 'Displays'],\n}: SymbolLibraryProps): React.ReactElement {\n const [selectedCategory, setSelectedCategory] = useState<string>('all');\n const [searchQuery, setSearchQuery] = useState('');\n\n const symbolIds = getAvailableSymbols();\n const allSymbols: SymbolDefinition[] = symbolIds\n .map((id) => getSymbolDefinition(id))\n .filter((symbol): symbol is SymbolDefinition => symbol !== undefined);\n\n // Map display category names to actual category values\n const categoryMap: Record<string, string> = {\n Valves: 'valve',\n Pumps: 'pump',\n Tanks: 'vessel',\n Instruments: 'instrument',\n Displays: 'display',\n };\n\n // Filter symbols by category and search\n const filteredSymbols = allSymbols.filter((symbol) => {\n const actualCategory = categoryMap[selectedCategory] || selectedCategory;\n const matchesCategory = selectedCategory === 'all' || symbol.category === actualCategory;\n\n const matchesSearch =\n searchQuery === '' ||\n symbol.name.toLowerCase().includes(searchQuery.toLowerCase()) ||\n symbol.id.toLowerCase().includes(searchQuery.toLowerCase());\n\n return matchesCategory && matchesSearch;\n });\n\n const handleDragStart = (symbol: SymbolDefinition, event: React.DragEvent) => {\n // Store symbol ID in drag data\n event.dataTransfer.setData('application/symbol-id', symbol.id);\n event.dataTransfer.effectAllowed = 'copy';\n onSymbolDragStart?.(symbol.id, event);\n };\n\n return (\n <div className={`symbol-library ${className}`.trim()} style={styles.container}>\n {/* Header */}\n <div style={styles.header}>\n <h3 style={styles.title}>Symbol Library</h3>\n </div>\n\n {/* Search */}\n <div style={styles.searchContainer}>\n <input\n type=\"text\"\n placeholder=\"Search symbols...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n style={styles.searchInput}\n />\n </div>\n\n {/* Category Filter */}\n {showCategories && (\n <div style={styles.categories}>\n <button\n onClick={() => setSelectedCategory('all')}\n style={{\n ...styles.categoryButton,\n ...(selectedCategory === 'all' ? styles.categoryButtonActive : {}),\n }}\n >\n All\n </button>\n {categories.map((category) => (\n <button\n key={category}\n onClick={() => setSelectedCategory(category)}\n style={{\n ...styles.categoryButton,\n ...(selectedCategory === category ? styles.categoryButtonActive : {}),\n }}\n >\n {category}\n </button>\n ))}\n </div>\n )}\n\n {/* Symbol Grid */}\n <div style={styles.symbolGrid}>\n {filteredSymbols.length === 0 ? (\n <div style={styles.emptyState}>No symbols found</div>\n ) : (\n filteredSymbols.map((symbol) => (\n <div\n key={symbol.id}\n draggable\n onDragStart={(e) => handleDragStart(symbol, e)}\n style={styles.symbolCard}\n title={symbol.metadata?.description || symbol.name}\n >\n {/* Symbol Preview */}\n <div style={styles.symbolPreview}>\n <svg\n viewBox={`${symbol.viewBox.x} ${symbol.viewBox.y} ${symbol.viewBox.width} ${symbol.viewBox.height}`}\n style={styles.symbolSvg}\n >\n <g dangerouslySetInnerHTML={{ __html: symbol.svgContent }} />\n </svg>\n </div>\n\n {/* Symbol Name */}\n <div style={styles.symbolName}>{symbol.name}</div>\n </div>\n ))\n )}\n </div>\n </div>\n );\n}\n\nconst styles: Record<string, React.CSSProperties> = {\n container: {\n display: 'flex',\n flexDirection: 'column',\n height: '100%',\n backgroundColor: '#ffffff',\n border: '1px solid #e0e0e0',\n borderRadius: '8px',\n overflow: 'hidden',\n },\n header: {\n padding: '16px',\n borderBottom: '1px solid #e0e0e0',\n backgroundColor: '#f5f5f5',\n },\n title: {\n margin: 0,\n fontSize: '16px',\n fontWeight: 600,\n color: '#263238',\n },\n searchContainer: {\n padding: '12px 16px',\n borderBottom: '1px solid #e0e0e0',\n },\n searchInput: {\n width: '100%',\n padding: '8px 12px',\n border: '1px solid #e0e0e0',\n borderRadius: '4px',\n fontSize: '14px',\n outline: 'none',\n },\n categories: {\n display: 'flex',\n gap: '8px',\n padding: '12px 16px',\n borderBottom: '1px solid #e0e0e0',\n flexWrap: 'wrap',\n },\n categoryButton: {\n padding: '6px 12px',\n border: '1px solid #e0e0e0',\n borderRadius: '4px',\n backgroundColor: '#ffffff',\n fontSize: '13px',\n cursor: 'pointer',\n transition: 'all 0.2s',\n },\n categoryButtonActive: {\n backgroundColor: '#3b82f6',\n color: '#ffffff',\n borderColor: '#3b82f6',\n },\n symbolGrid: {\n flex: 1,\n padding: '16px',\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(100px, 1fr))',\n gap: '12px',\n overflowY: 'auto',\n alignContent: 'start',\n },\n emptyState: {\n gridColumn: '1 / -1',\n textAlign: 'center',\n padding: '32px',\n color: '#999',\n },\n symbolCard: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n padding: '12px',\n border: '1px solid #e0e0e0',\n borderRadius: '6px',\n backgroundColor: '#ffffff',\n cursor: 'grab',\n transition: 'all 0.2s',\n },\n symbolPreview: {\n width: '60px',\n height: '60px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n marginBottom: '8px',\n },\n symbolSvg: {\n maxWidth: '100%',\n maxHeight: '100%',\n },\n symbolName: {\n fontSize: '12px',\n color: '#263238',\n textAlign: 'center',\n wordBreak: 'break-word',\n lineHeight: '1.3',\n },\n};\n","import React from 'react';\nimport type { NodeInstance } from '../../diagram/schema';\nimport type { TelemetryBinding } from '../../diagram/hooks/useTelemetry';\nimport {\n getAppearanceSchema,\n readAppearanceValue,\n writeAppearanceValue,\n} from '../../diagram/symbols';\nimport type { AppearanceField } from '../../diagram/symbols';\n\nexport interface NodeConfigPanelProps {\n /** Selected node to configure */\n node: NodeInstance;\n /** Telemetry binding for the node (if any) */\n binding: TelemetryBinding | null;\n /** Callback when node properties are updated */\n onUpdateNode: (updates: Partial<NodeInstance>) => void;\n /** Callback when telemetry is added/updated */\n onUpdateTelemetry: (updates: any) => void;\n /** Callback when telemetry binding is added for the first time */\n onAddTelemetry?: () => void;\n /** Callback to remove/clear the telemetry binding for this node */\n onRemoveTelemetry?: () => void;\n /** Known signal/tag names, used to autocomplete and validate 'tag' fields. */\n availableTags?: string[];\n /** Callback when node is removed */\n onRemove: () => void;\n /** Additional CSS class name */\n className?: string;\n /** Custom styles */\n style?: React.CSSProperties;\n}\n\n/**\n * NodeConfigPanel - Reusable configuration panel for editing node properties and telemetry\n *\n * @example\n * ```tsx\n * <NodeConfigPanel\n * node={selectedNode}\n * binding={telemetry.get(selectedNode.id)}\n * onUpdateNode={(updates) => updateNode(selectedNode.id, updates)}\n * onUpdateTelemetry={(updates) => updateTelemetryBatch([updates])}\n * onRemove={() => removeNode(selectedNode.id)}\n * />\n * ```\n */\nexport function NodeConfigPanel({\n node,\n binding,\n onUpdateNode,\n onUpdateTelemetry,\n onAddTelemetry,\n onRemoveTelemetry,\n availableTags = [],\n onRemove,\n className = '',\n style = {},\n}: NodeConfigPanelProps) {\n const defaultStyle: React.CSSProperties = {\n width: '320px',\n backgroundColor: '#f8fafc',\n borderLeft: '1px solid #e2e8f0',\n overflow: 'auto',\n padding: '20px',\n ...style,\n };\n\n const appearanceFields = getAppearanceSchema(node.symbolId);\n const showGenericTelemetry = appearanceFields.length === 0;\n\n return (\n <div className={className} style={defaultStyle}>\n <h2 style={{ margin: '0 0 16px 0', fontSize: '1rem', fontWeight: 600 }}>\n Configure: {node.id}\n </h2>\n\n {/* Node Settings */}\n <div style={{ marginBottom: '20px' }}>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '8px' }}>\n Node Settings\n </h3>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>Label:</label>\n <input\n type=\"text\"\n value={node.label || node.id}\n onChange={(e) => onUpdateNode({ label: e.target.value })}\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n marginBottom: '8px',\n }}\n />\n\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}>\n <div>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Label Offset X:\n </label>\n <input\n type=\"number\"\n value={node.labelOffset?.x ?? 0}\n onChange={(e) =>\n onUpdateNode({\n labelOffset: {\n x: parseFloat(e.target.value) || 0,\n y: node.labelOffset?.y ?? 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n }}\n />\n </div>\n <div>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Label Offset Y:\n </label>\n <input\n type=\"number\"\n value={node.labelOffset?.y ?? 0}\n onChange={(e) =>\n onUpdateNode({\n labelOffset: {\n x: node.labelOffset?.x ?? 0,\n y: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n }}\n />\n </div>\n </div>\n\n <div>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Label Font Size:\n </label>\n <input\n type=\"number\"\n min=\"6\"\n max=\"24\"\n value={node.labelFontSize ?? 10}\n onChange={(e) =>\n onUpdateNode({\n labelFontSize: parseFloat(e.target.value) || 10,\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n }}\n />\n </div>\n\n <div>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Rotation (degrees):\n </label>\n <input\n type=\"number\"\n min=\"0\"\n max=\"360\"\n step=\"15\"\n value={node.transform.rotation ?? 0}\n onChange={(e) =>\n onUpdateNode({\n transform: {\n ...node.transform,\n rotation: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n }}\n />\n </div>\n </div>\n\n {/* Appearance */}\n {appearanceFields.length > 0 && (\n <div style={{ marginBottom: '20px' }}>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '8px' }}>\n Appearance\n </h3>\n\n {appearanceFields.map((field: AppearanceField) => {\n const value = readAppearanceValue(node, field);\n const fieldLabel = field.label ?? field.key;\n const inputStyle = {\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n } as const;\n\n let control: React.ReactNode;\n if (field.kind === 'boolean') {\n control = (\n <label style={{ fontSize: '0.75rem', display: 'block' }}>\n <input\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={(e) => onUpdateNode(writeAppearanceValue(node, field, e.target.checked))}\n style={{ marginRight: '6px' }}\n />\n {fieldLabel}\n </label>\n );\n } else if (field.kind === 'select') {\n control = (\n <>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {fieldLabel}:\n </label>\n <select\n value={String(value)}\n onChange={(e) => onUpdateNode(writeAppearanceValue(node, field, e.target.value))}\n style={inputStyle}\n >\n {field.options?.map((opt) => (\n <option key={opt} value={opt}>\n {opt}\n </option>\n ))}\n </select>\n </>\n );\n } else if (field.kind === 'number') {\n control = (\n <>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {fieldLabel}:\n </label>\n <input\n type=\"number\"\n value={Number(value)}\n onChange={(e) =>\n onUpdateNode(writeAppearanceValue(node, field, parseFloat(e.target.value) || 0))\n }\n style={inputStyle}\n />\n </>\n );\n } else if (field.kind === 'tag') {\n const tagValue = String(value);\n const listId = `tags-${node.id}-${field.key}`;\n const known = tagValue.length > 0 && availableTags.includes(tagValue);\n const unknown = tagValue.length > 0 && !known;\n control = (\n <>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {fieldLabel}:\n </label>\n <input\n type=\"text\"\n value={tagValue}\n list={listId}\n onChange={(e) => onUpdateNode(writeAppearanceValue(node, field, e.target.value))}\n placeholder=\"type or pick a signal…\"\n style={{\n ...inputStyle,\n border: `1px solid ${unknown ? '#f59e0b' : known ? '#22c55e' : '#cbd5e1'}`,\n }}\n />\n <datalist id={listId}>\n {availableTags.map((t) => (\n <option key={t} value={t} />\n ))}\n </datalist>\n {tagValue.length > 0 && (\n <div\n style={{\n fontSize: '0.7rem',\n marginTop: '3px',\n color: known ? '#16a34a' : '#b45309',\n }}\n >\n {known\n ? '✓ matched signal'\n : '⚠ no matching signal (reads as false until published)'}\n </div>\n )}\n </>\n );\n } else {\n control = (\n <>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {fieldLabel}:\n </label>\n <input\n type=\"text\"\n value={String(value)}\n onChange={(e) => onUpdateNode(writeAppearanceValue(node, field, e.target.value))}\n style={inputStyle}\n />\n </>\n );\n }\n\n return (\n <div key={field.key} style={{ marginBottom: '10px' }}>\n {control}\n {field.help && (\n <div\n style={{\n fontSize: '0.7rem',\n color: '#64748b',\n marginTop: '3px',\n lineHeight: 1.35,\n }}\n >\n {field.help}\n </div>\n )}\n </div>\n );\n })}\n </div>\n )}\n\n {/* Telemetry Configuration */}\n {showGenericTelemetry && (\n <div style={{ marginBottom: '20px' }}>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '8px' }}>\n Telemetry Data\n </h3>\n\n {!binding ? (\n <button\n onClick={() => {\n if (onAddTelemetry) {\n onAddTelemetry();\n } else {\n // Default telemetry initialization\n onUpdateTelemetry({\n nodeId: node.id,\n tag: node.id,\n value: {\n value: 50,\n status: 'normal',\n unit: 'PSI',\n timestamp: Date.now(),\n },\n history: [45, 47, 48, 50, 52, 51, 49, 50],\n display: {\n showValue: true,\n showSparkline: false,\n precision: 1,\n backgroundColor: 'status',\n },\n thresholds: {\n highHigh: 85,\n high: 70,\n low: 30,\n lowLow: 15,\n },\n });\n }\n }}\n style={{\n padding: '8px 16px',\n backgroundColor: '#3b82f6',\n color: '#fff',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n width: '100%',\n }}\n >\n + Add Telemetry\n </button>\n ) : (\n <>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Tag (attribute):\n </label>\n <input\n type=\"text\"\n value={binding.tag || ''}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: e.target.value,\n })\n }\n placeholder=\"e.g. pump.running\"\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n marginBottom: '8px',\n }}\n />\n\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Initial Value:\n </label>\n <input\n type=\"number\"\n value={binding.value.value as number}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n value: {\n ...binding.value,\n value: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n marginBottom: '8px',\n }}\n />\n\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Unit:\n </label>\n <input\n type=\"text\"\n value={binding.value.unit || ''}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n value: {\n ...binding.value,\n unit: e.target.value,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n marginBottom: '8px',\n }}\n />\n\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n <input\n type=\"checkbox\"\n checked={binding.display?.showSparkline ?? false}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n showSparkline: e.target.checked,\n },\n })\n }\n style={{ marginRight: '6px' }}\n />\n Show Sparkline\n </label>\n\n <div style={{ marginTop: '12px' }}>\n <label\n style={{\n fontSize: '0.75rem',\n fontWeight: 600,\n display: 'block',\n marginBottom: '8px',\n }}\n >\n Telemetry Position Offset:\n </label>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}>\n <div>\n <label style={{ fontSize: '0.7rem', display: 'block', marginBottom: '4px' }}>\n X Offset:\n </label>\n <input\n type=\"number\"\n value={binding.display?.offsetX ?? 0}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n offsetX: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '4px 6px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.75rem',\n }}\n />\n </div>\n <div>\n <label style={{ fontSize: '0.7rem', display: 'block', marginBottom: '4px' }}>\n Y Offset:\n </label>\n <input\n type=\"number\"\n value={binding.display?.offsetY ?? 0}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n offsetY: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '4px 6px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.75rem',\n }}\n />\n </div>\n </div>\n {/* Text & Sparkline Colors */}\n <div style={{ marginTop: '16px' }}>\n <label\n style={{\n fontSize: '0.75rem',\n fontWeight: 600,\n display: 'block',\n marginBottom: '8px',\n }}\n >\n Display Colors:\n </label>\n\n {/* Text Color */}\n <div style={{ marginBottom: '8px' }}>\n <label style={{ fontSize: '0.7rem', display: 'block', marginBottom: '4px' }}>\n Text Color:\n </label>\n <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>\n <input\n type=\"color\"\n value={binding.display?.textColor ?? '#000000'}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n textColor: e.target.value,\n },\n })\n }\n style={{\n width: '40px',\n height: '28px',\n border: '1px solid #cbd5e1',\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n />\n <input\n type=\"text\"\n value={binding.display?.textColor ?? '#000000'}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n textColor: e.target.value,\n },\n })\n }\n placeholder=\"#000000\"\n style={{\n flex: 1,\n padding: '4px 6px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.7rem',\n fontFamily: 'monospace',\n }}\n />\n </div>\n </div>\n\n {/* Sparkline Color */}\n {binding.display?.showSparkline && (\n <div style={{ marginBottom: '8px' }}>\n <label style={{ fontSize: '0.7rem', display: 'block', marginBottom: '4px' }}>\n Sparkline Color:\n </label>\n <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>\n <input\n type=\"color\"\n value={binding.display?.sparklineColor ?? '#3b82f6'}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n sparklineColor: e.target.value,\n },\n })\n }\n style={{\n width: '40px',\n height: '28px',\n border: '1px solid #cbd5e1',\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n />\n <input\n type=\"text\"\n value={binding.display?.sparklineColor ?? '#3b82f6'}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n sparklineColor: e.target.value,\n },\n })\n }\n placeholder=\"#3b82f6\"\n style={{\n flex: 1,\n padding: '4px 6px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.7rem',\n fontFamily: 'monospace',\n }}\n />\n </div>\n </div>\n )}\n\n {/* Background Color */}\n <div style={{ marginBottom: '8px' }}>\n <label style={{ fontSize: '0.7rem', display: 'block', marginBottom: '4px' }}>\n Background:\n </label>\n <input\n type=\"text\"\n value={binding.display?.backgroundColor ?? 'status'}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n backgroundColor: e.target.value,\n },\n })\n }\n placeholder=\"status or #ffffff\"\n style={{\n width: '100%',\n padding: '4px 6px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.7rem',\n fontFamily: 'monospace',\n }}\n />\n <div style={{ fontSize: '0.65rem', color: '#6b7280', marginTop: '2px' }}>\n Use 'status' for status-based color or hex code\n </div>\n </div>\n </div>{' '}\n </div>\n\n {onRemoveTelemetry && (\n <button\n onClick={onRemoveTelemetry}\n style={{\n width: '100%',\n padding: '6px 8px',\n marginTop: '8px',\n borderRadius: '4px',\n border: '1px solid #ef4444',\n background: 'transparent',\n color: '#ef4444',\n fontSize: '0.8125rem',\n cursor: 'pointer',\n }}\n >\n Remove Telemetry\n </button>\n )}\n </>\n )}\n </div>\n )}\n\n {/* Thresholds (if telemetry exists) */}\n {showGenericTelemetry && binding && (\n <div style={{ marginBottom: '20px' }}>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '8px' }}>Thresholds</h3>\n\n {(['highHigh', 'high', 'low', 'lowLow'] as const).map((key) => {\n const label =\n key === 'highHigh'\n ? 'High-High (≥)'\n : key === 'high'\n ? 'High (≥)'\n : key === 'low'\n ? 'Low (≤)'\n : 'Low-Low (≤)';\n return (\n <div key={key} style={{ marginBottom: '8px' }}>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {label}\n </label>\n <input\n type=\"number\"\n value={\n binding.thresholds?.[key] ??\n (key === 'highHigh' ? 85 : key === 'high' ? 70 : key === 'low' ? 30 : 15)\n }\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n thresholds: {\n ...binding.thresholds,\n [key]: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n }}\n />\n </div>\n );\n })}\n </div>\n )}\n\n {/* Status Colors (if telemetry exists) */}\n {showGenericTelemetry && binding && (\n <div style={{ marginBottom: '20px' }}>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '8px' }}>\n Status Colors\n </h3>\n\n {(['normal', 'warning', 'alarm', 'fault', 'off'] as const).map((status) => {\n const defaultColors = {\n normal: '#10b981',\n warning: '#f59e0b',\n alarm: '#ef4444',\n fault: '#8b5cf6',\n off: '#6b7280',\n };\n const label =\n status === 'normal'\n ? 'Normal'\n : status === 'warning'\n ? 'Warning'\n : status === 'alarm'\n ? 'Alarm'\n : status === 'fault'\n ? 'Fault'\n : 'Off';\n return (\n <div key={status} style={{ marginBottom: '8px' }}>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {label}:\n </label>\n <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>\n <input\n type=\"color\"\n value={binding.statusColors?.[status] ?? defaultColors[status]}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n statusColors: {\n ...binding.statusColors,\n [status]: e.target.value,\n },\n })\n }\n style={{\n width: '50px',\n height: '32px',\n border: '1px solid #cbd5e1',\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n />\n <input\n type=\"text\"\n value={binding.statusColors?.[status] ?? defaultColors[status]}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n statusColors: {\n ...binding.statusColors,\n [status]: e.target.value,\n },\n })\n }\n placeholder={defaultColors[status]}\n style={{\n flex: 1,\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.75rem',\n fontFamily: 'monospace',\n }}\n />\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {/* Remove Node */}\n <div style={{ marginTop: '20px', paddingTop: '20px', borderTop: '1px solid #cbd5e1' }}>\n <button\n onClick={onRemove}\n style={{\n padding: '8px 16px',\n backgroundColor: '#ef4444',\n color: '#fff',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n width: '100%',\n }}\n >\n Remove Node\n </button>\n </div>\n </div>\n );\n}\n","import type { Diagram } from '../schema';\n\n/**\n * Validates a diagram object structure\n */\nexport function validateDiagram(diagram: unknown): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (!diagram || typeof diagram !== 'object') {\n errors.push('Diagram must be an object');\n return { valid: false, errors };\n }\n\n const d = diagram as Partial<Diagram>;\n\n // Required fields\n if (!d.id || typeof d.id !== 'string') {\n errors.push('Diagram must have a valid id (string)');\n }\n if (!d.name || typeof d.name !== 'string') {\n errors.push('Diagram must have a valid name (string)');\n }\n if (!d.version || typeof d.version !== 'string') {\n errors.push('Diagram must have a valid version (string)');\n }\n\n // ViewBox validation\n if (!d.viewBox) {\n errors.push('Diagram must have a viewBox');\n } else if (\n typeof d.viewBox.x !== 'number' ||\n typeof d.viewBox.y !== 'number' ||\n typeof d.viewBox.width !== 'number' ||\n typeof d.viewBox.height !== 'number'\n ) {\n errors.push('ViewBox must have numeric x, y, width, height properties');\n }\n\n // Array validations\n if (!Array.isArray(d.nodes)) {\n errors.push('Diagram.nodes must be an array');\n }\n if (!Array.isArray(d.pipes)) {\n errors.push('Diagram.pipes must be an array');\n }\n if (!Array.isArray(d.overlays)) {\n errors.push('Diagram.overlays must be an array');\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * Serializes a diagram to JSON string\n */\nexport function exportDiagram(diagram: Diagram): string {\n return JSON.stringify(diagram, null, 2);\n}\n\n/**\n * Parses a diagram from JSON string\n */\nexport function importDiagram(json: string): Diagram {\n try {\n const parsed = JSON.parse(json);\n const validation = validateDiagram(parsed);\n\n if (!validation.valid) {\n throw new Error(`Invalid diagram: ${validation.errors.join(', ')}`);\n }\n\n return parsed as Diagram;\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new Error(`Invalid JSON: ${error.message}`);\n }\n throw error;\n }\n}\n","import type { Diagram } from '../schema';\nimport { generateRoutePoints } from '../utils/portCalculations';\n\n// Define nodes first so we can calculate pipe routes\nconst nodes = [\n {\n id: 'V-101',\n symbolId: 'valve-ball',\n transform: {\n x: 100,\n y: 150,\n rotation: 0,\n },\n ports: [\n { id: 'inlet', x: -25, y: 0, type: 'inlet' as const },\n { id: 'outlet', x: 25, y: 0, type: 'outlet' as const },\n ],\n },\n {\n id: 'P-101',\n symbolId: 'pump-centrifugal',\n transform: {\n x: 250,\n y: 150,\n rotation: 0,\n },\n ports: [\n { id: 'inlet', x: -40, y: 0, type: 'inlet' as const },\n { id: 'outlet', x: 40, y: 0, type: 'outlet' as const },\n ],\n },\n {\n id: 'T-101',\n symbolId: 'vessel-tank',\n transform: {\n x: 450,\n y: 120,\n rotation: 0,\n },\n ports: [\n { id: 'inlet', x: 0, y: -40, type: 'inlet' as const },\n { id: 'outlet', x: 0, y: 40, type: 'outlet' as const },\n ],\n },\n];\n\n/**\n * Example P&ID diagram with a simple valve-pump-tank flow\n * Uses automatic port-to-port connection routing\n */\nexport const exampleDiagram: Diagram = {\n id: 'example-001',\n name: 'Simple Process Flow',\n version: '1.0.0',\n viewBox: {\n x: -100,\n y: -50,\n width: 1200,\n height: 600,\n },\n nodes,\n pipes: [\n {\n id: 'pipe-001',\n fromNodeId: 'V-101',\n fromPortId: 'outlet',\n toNodeId: 'P-101',\n toPortId: 'inlet',\n routePoints: generateRoutePoints(nodes[0], 'outlet', nodes[1], 'inlet'),\n style: {\n stroke: '#0277bd',\n strokeWidth: 4,\n },\n },\n {\n id: 'pipe-002',\n fromNodeId: 'P-101',\n fromPortId: 'outlet',\n toNodeId: 'T-101',\n toPortId: 'inlet',\n routePoints: generateRoutePoints(nodes[1], 'outlet', nodes[2], 'inlet', {\n style: 'orthogonal',\n }),\n style: {\n stroke: '#2e7d32',\n strokeWidth: 4,\n },\n },\n ],\n overlays: [\n {\n id: 'overlay-v101',\n type: 'value',\n anchorType: 'node',\n anchorId: 'V-101',\n offset: { x: 0, y: -40 },\n binding: {\n sourceType: 'tag',\n source: 'V101.position',\n format: '0.0%',\n },\n content: {\n text: 'Valve Position',\n },\n },\n {\n id: 'overlay-p101',\n type: 'value',\n anchorType: 'node',\n anchorId: 'P-101',\n offset: { x: 0, y: -50 },\n binding: {\n sourceType: 'tag',\n source: 'P101.flow',\n format: '0.0 LPM',\n },\n content: {\n text: 'Flow Rate',\n },\n },\n {\n id: 'overlay-t101',\n type: 'gauge',\n anchorType: 'node',\n anchorId: 'T-101',\n offset: { x: 60, y: 0 },\n binding: {\n sourceType: 'tag',\n source: 'T101.level',\n },\n content: {\n component: 'CircularGauge',\n props: {\n max: 100,\n unit: '%',\n label: 'Level',\n },\n },\n },\n ],\n metadata: {\n author: 'System',\n created: '2025-12-31T00:00:00Z',\n description: 'Example P&ID diagram demonstrating schema structure',\n tags: ['example', 'tutorial'],\n },\n};\n","/**\n * Telemetry status type\n */\nexport type TelemetryStatus = 'normal' | 'warning' | 'alarm' | 'fault' | 'off';\n\n/**\n * Threshold configuration\n */\nexport interface Thresholds {\n highHigh?: number;\n high?: number;\n low?: number;\n lowLow?: number;\n}\n\n/**\n * Calculate status based on value and thresholds\n * @param value - Current numeric value\n * @param thresholds - Threshold configuration\n * @returns Status string\n */\nexport function calculateThresholdStatus(\n value: number,\n thresholds?: Thresholds\n): TelemetryStatus {\n if (!thresholds) return 'normal';\n\n const { highHigh, high, low, lowLow } = thresholds;\n\n // Check high-high alarm (highest priority)\n if (highHigh !== undefined && value >= highHigh) {\n return 'alarm';\n }\n\n // Check low-low fault\n if (lowLow !== undefined && value <= lowLow) {\n return 'fault';\n }\n\n // Check high warning\n if (high !== undefined && value >= high) {\n return 'warning';\n }\n\n // Check low warning\n if (low !== undefined && value <= low) {\n return 'warning';\n }\n\n return 'normal';\n}\n\n/**\n * Default thresholds for typical industrial processes (percentage-based)\n */\nexport const DEFAULT_THRESHOLDS: Thresholds = {\n highHigh: 85,\n high: 70,\n low: 30,\n lowLow: 15,\n};\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { ForcedState } from './TelemetryBinding';\n\n/**\n * A custom action button rendered in the device control panel (e.g. Tare, Zero).\n */\nexport interface CustomAction {\n /** Stable action id passed to onCustomAction when clicked. */\n id: string;\n /** Button label. */\n label: string;\n /** Optional leading icon / emoji. */\n icon?: string;\n /** Visual variant (reserved for future styling). */\n variant?: 'primary' | 'secondary' | 'alarm' | 'warning';\n /**\n * When true, the button renders disabled and does NOT fire onCustomAction.\n * Use for actions that are only conditionally available (e.g. a Tare that is\n * not ready).\n */\n disabled?: boolean;\n /**\n * Tooltip shown on hover explaining why the action is unavailable. Only\n * meaningful when `disabled` is true.\n */\n disabledReason?: string;\n}\n\n/**\n * Device control capabilities and commands\n */\nexport interface ControlCapabilities {\n /**\n * Can the device be started? When `canStart` or `canStop` is true, the\n * panel shows BOTH the START and the STOP button, each in a fixed place\n * (alphinity-tffi #3853). A false `canStart` then shows START disabled.\n * It does not remove START and it does not let STOP take its place.\n */\n canStart?: boolean;\n /** Can the device be stopped? See `canStart`. */\n canStop?: boolean;\n /**\n * Does START apply now? Default: true (enabled). Set false to show START\n * disabled, for example on a pump that runs already. Use this with\n * `canStart: true` to keep \"the device has a START\" apart from \"START\n * applies now\". A disabled START does not call `onStart`.\n */\n startEnabled?: boolean;\n /**\n * Does STOP apply now? Default: true (enabled). Set false to show STOP\n * disabled, for example on a pump that is stopped. The panel itself never\n * disables STOP for a pending command. A disabled STOP does not call\n * `onStop`.\n */\n stopEnabled?: boolean;\n /** Can the device be paused? */\n canPause?: boolean;\n /** Can the device be reset? */\n canReset?: boolean;\n /** Custom actions available */\n customActions?: CustomAction[];\n}\n\n/**\n * Device control mode option\n */\nexport interface ControlMode {\n /** Mode value/ID */\n value: string;\n /** Display label */\n label: string;\n /** Description or context about this mode */\n description?: string;\n /** Whether this mode is currently available */\n enabled?: boolean;\n /** Icon or emoji to display (optional) */\n icon?: string;\n /** Mode-specific capabilities (overrides binding-level capabilities when mode is active) */\n capabilities?: ControlCapabilities;\n}\n\n/**\n * Control parameter definition (setpoint, adjustment, etc.)\n */\nexport interface ControlParameter {\n /** Parameter ID */\n id: string;\n /** Display label */\n label: string;\n /** Unit of measurement */\n unit?: string;\n /** Minimum allowed value */\n min?: number;\n /** Maximum allowed value */\n max?: number;\n /** Step increment */\n step?: number;\n /** Decimal precision for display */\n precision?: number;\n /** Current value */\n value: number | string | boolean;\n /**\n * Maintenance force state for this parameter (#3607, R19). Absent or\n * `active: false` means `value` is a real reading. See `ForcedState`.\n */\n forced?: ForcedState;\n /** Whether parameter is read-only */\n readOnly?: boolean;\n /** Parameter type */\n type?: 'number' | 'string' | 'boolean' | 'select';\n /** Options for select type */\n options?: Array<{ value: string | number; label: string }>;\n /** Custom validation function */\n validate?: (value: any) => boolean | string;\n /** Modes where this parameter is visible (if empty/undefined, visible in all modes) */\n modes?: string[];\n}\n\n/**\n * Device control state\n */\nexport interface ControlState {\n /** Current operational mode */\n currentMode: string;\n /** Is device currently running? */\n isRunning?: boolean;\n /** Is device in alarm state? */\n hasAlarm?: boolean;\n /**\n * Current status. `unknown` marks an untrusted read (OPC UA StatusCode\n * severity Bad). This library never infers `unknown`; it only displays a\n * value the backend has already published as `unknown`.\n *\n * `forced` marks a device that a maintenance force holds. It resolves to\n * `FORCED_MARK_COLOR`, the same violet as every other forced mark.\n */\n status?:\n | 'normal'\n | 'warning'\n | 'alarm'\n | 'fault'\n | 'off'\n | 'starting'\n | 'stopping'\n | 'unknown'\n | 'forced';\n /** Status message */\n statusMessage?: string;\n /** Last command timestamp */\n lastCommandTime?: number;\n /** Last command result */\n lastCommandResult?: {\n success: boolean;\n message?: string;\n };\n}\n\n/**\n * Device control binding - maps a node to device control capabilities\n *\n * Similar to TelemetryBinding but for device commands and setpoints\n */\nexport interface DeviceControlBinding {\n /** Node ID this control is bound to */\n nodeId: string;\n\n /** Device tag/ID for backend communication */\n tag: string;\n\n /** Human-readable device name */\n deviceName?: string;\n\n /** Device type (pump, valve, mixer, etc.) */\n deviceType?: string;\n\n /** Available control modes */\n modes: ControlMode[];\n\n /** Current device state */\n state: ControlState;\n\n /** Control parameters (setpoints, adjustments) organized by mode */\n parameters: Record<string, ControlParameter[]>;\n\n /** Device capabilities (what commands are available) */\n capabilities?: ControlCapabilities;\n\n /** Callbacks for control actions */\n actions: {\n /** Called when mode changes */\n onModeChange?: (mode: string) => void | Promise<void>;\n /** Called when parameter value changes */\n onParameterChange?: (parameterId: string, value: any) => void | Promise<void>;\n /** Called when start button clicked */\n onStart?: () => void | Promise<void>;\n /** Called when stop button clicked */\n onStop?: () => void | Promise<void>;\n /** Called when pause button clicked */\n onPause?: () => void | Promise<void>;\n /** Called when reset button clicked */\n onReset?: () => void | Promise<void>;\n /** Called for custom actions */\n onCustomAction?: (actionId: string) => void | Promise<void>;\n };\n\n /** Display customization */\n display?: {\n /** Panel title (defaults to deviceName or nodeId) */\n title?: string;\n /** Show mode selector */\n showModeSelector?: boolean;\n /** Show start/stop buttons */\n showStartStop?: boolean;\n /** Panel size */\n size?: 'small' | 'medium' | 'large';\n /** Custom CSS class name */\n className?: string;\n /** Custom inline styles */\n style?: React.CSSProperties;\n /** Group parameters by category */\n groupParameters?: boolean;\n /**\n * Layout of the custom action buttons.\n * - 'wrap' (default): each action has a 120px minimum width and the\n * actions wrap onto more rows when the panel is narrow.\n * - 'row': all actions share ONE row with no minimum width and a small\n * side padding, so several actions fit in a narrow faceplate. A long\n * label can wrap onto two lines inside its button.\n */\n customActionsLayout?: 'wrap' | 'row';\n };\n\n /** Styling overrides */\n theme?: {\n /** Primary color */\n primaryColor?: string;\n /** Secondary color */\n secondaryColor?: string;\n /** Accent color */\n accentColor?: string;\n /** Background color */\n backgroundColor?: string;\n /** Text color */\n textColor?: string;\n /** Status colors */\n statusColors?: {\n normal?: string;\n warning?: string;\n alarm?: string;\n fault?: string;\n off?: string;\n starting?: string;\n stopping?: string;\n unknown?: string;\n };\n };\n}\n\n/**\n * Map of device control bindings by node ID\n */\nexport type DeviceControlMap = Map<string, DeviceControlBinding>;\n\n/**\n * Helper to create a control binding from backend device configuration\n */\nexport function createControlBinding(\n nodeId: string,\n deviceConfig: {\n tag: string;\n name?: string;\n type?: string;\n modes: Array<{ value: string; label: string; enabled?: boolean }>;\n currentMode: string;\n parameters: Array<{\n id: string;\n label: string;\n unit?: string;\n min?: number;\n max?: number;\n value: any;\n modes?: string[]; // Which modes this parameter applies to\n }>;\n capabilities?: {\n canStart?: boolean;\n canStop?: boolean;\n customActions?: CustomAction[];\n };\n status?: string;\n isRunning?: boolean;\n },\n actions: DeviceControlBinding['actions']\n): DeviceControlBinding {\n // Organize parameters by mode\n const parametersByMode: Record<string, ControlParameter[]> = {};\n\n deviceConfig.parameters.forEach((param) => {\n const modes = param.modes || deviceConfig.modes.map((m) => m.value);\n\n modes.forEach((mode) => {\n if (!parametersByMode[mode]) {\n parametersByMode[mode] = [];\n }\n\n parametersByMode[mode].push({\n id: param.id,\n label: param.label,\n unit: param.unit,\n min: param.min,\n max: param.max,\n value: param.value,\n type: typeof param.value === 'number' ? 'number' : 'string',\n });\n });\n });\n\n return {\n nodeId,\n tag: deviceConfig.tag,\n deviceName: deviceConfig.name,\n deviceType: deviceConfig.type,\n modes: deviceConfig.modes,\n state: {\n currentMode: deviceConfig.currentMode,\n isRunning: deviceConfig.isRunning,\n status: deviceConfig.status as any,\n },\n parameters: parametersByMode,\n capabilities: deviceConfig.capabilities,\n actions,\n };\n}\n","import { useState, useCallback, useRef } from 'react';\nimport type { DeviceControlBinding, DeviceControlMap } from '../diagram/schema/DeviceControlBinding';\n\n/** Kinds of command a node can have in flight at once. */\nexport type CommandKind = 'start' | 'stop' | 'mode' | 'parameter' | 'action';\n\nexport interface UseDeviceControlsOptions {\n /**\n * If set (> 0), a command whose action callback does not settle within this\n * many milliseconds is abandoned: the in-flight marker clears and a timeout\n * result is recorded as command feedback. This only bounds how long the\n * optional \"sending…\" hint shows — controls are never disabled, so a hung\n * command can never lock anyone out regardless of this value.\n *\n * Note: this is a UI-level abort — it stops the HMI from *waiting*, but does\n * not cancel an already-dispatched command (that would require the action to\n * honor an AbortSignal). The command may still land; telemetry remains the\n * source of truth for the resulting device state.\n */\n commandTimeoutMs?: number;\n}\n\n/** Stable in-flight key for a (node, command) pair. */\nfunction commandKey(nodeId: string, kind: CommandKind, suffix?: string): string {\n return suffix ? `${nodeId}:${kind}:${suffix}` : `${nodeId}:${kind}`;\n}\n\n/**\n * Hook for managing device control bindings\n * Similar pattern to useTelemetry\n */\nexport function useDeviceControls(\n initialBindings?: DeviceControlBinding[],\n options?: UseDeviceControlsOptions\n) {\n const { commandTimeoutMs } = options ?? {};\n\n const [controls, setControls] = useState<DeviceControlMap>(() => {\n const map = new Map();\n if (initialBindings) {\n initialBindings.forEach((binding) => {\n map.set(binding.nodeId, binding);\n });\n }\n return map;\n });\n\n // In-flight command tracking. This is a VISIBILITY hint only — interaction\n // state, never device state, and it never gates operator agency: it does not\n // disable buttons or block commands (the HMI fails open; see the fail-open\n // principle). It exists solely so a caller can show a \"sending…\" affordance.\n // `pendingRef` is a per-key in-flight count (commands re-send freely, so the\n // same command can be in flight more than once); `pendingCommands` is the\n // reactive set of keys with a non-zero count, exposed via `isCommandPending`.\n const pendingRef = useRef<Map<string, number>>(new Map());\n const [pendingCommands, setPendingCommands] = useState<Set<string>>(() => new Set());\n\n const syncPending = useCallback(() => {\n setPendingCommands(new Set(pendingRef.current.keys()));\n }, []);\n\n /**\n * Update a specific control binding\n */\n const updateControl = useCallback((nodeId: string, updates: Partial<DeviceControlBinding>) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n \n if (existing) {\n newMap.set(nodeId, { ...existing, ...updates });\n } else {\n // If binding doesn't exist, we need at least nodeId, tag, modes, state, parameters, actions\n if ('tag' in updates && 'modes' in updates && 'state' in updates && 'parameters' in updates && 'actions' in updates) {\n newMap.set(nodeId, { nodeId, ...updates } as DeviceControlBinding);\n }\n }\n \n return newMap;\n });\n }, []);\n\n /**\n * Update multiple bindings at once\n */\n const updateControlBatch = useCallback((updates: Record<string, Partial<DeviceControlBinding>>) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n \n Object.entries(updates).forEach(([nodeId, update]) => {\n const existing = newMap.get(nodeId);\n if (existing) {\n newMap.set(nodeId, { ...existing, ...update });\n }\n });\n \n return newMap;\n });\n }, []);\n\n /**\n * Update device state (mode, status, running state, etc.)\n */\n const updateDeviceState = useCallback(\n (nodeId: string, stateUpdates: Partial<DeviceControlBinding['state']>) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n \n if (existing) {\n newMap.set(nodeId, {\n ...existing,\n state: { ...existing.state, ...stateUpdates },\n });\n }\n \n return newMap;\n });\n },\n []\n );\n\n /**\n * Update a single parameter value\n */\n const updateParameter = useCallback(\n (nodeId: string, parameterId: string, value: any) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n \n if (existing) {\n const updatedParameters = { ...existing.parameters };\n \n // Update parameter in all modes where it exists\n Object.keys(updatedParameters).forEach((mode) => {\n const paramIndex = updatedParameters[mode].findIndex((p) => p.id === parameterId);\n if (paramIndex !== -1) {\n updatedParameters[mode] = [...updatedParameters[mode]];\n updatedParameters[mode][paramIndex] = {\n ...updatedParameters[mode][paramIndex],\n value,\n };\n }\n });\n \n newMap.set(nodeId, {\n ...existing,\n parameters: updatedParameters,\n });\n }\n \n return newMap;\n });\n },\n []\n );\n\n /**\n * Set control binding (add or replace)\n */\n const setControlBinding = useCallback((binding: DeviceControlBinding) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n newMap.set(binding.nodeId, binding);\n return newMap;\n });\n }, []);\n\n /**\n * Remove control binding\n */\n const removeControlBinding = useCallback((nodeId: string) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n newMap.delete(nodeId);\n return newMap;\n });\n }, []);\n\n /**\n * Get control binding for a specific node\n */\n const getControlBinding = useCallback(\n (nodeId: string): DeviceControlBinding | undefined => {\n return controls.get(nodeId);\n },\n [controls]\n );\n\n /**\n * Is there a command in flight for this node?\n *\n * Pass a `kind` to ask about a specific command (e.g. `'start'`), or omit it\n * to ask whether *any* command is pending for the node. Backed by reactive\n * state so components re-render when a command starts/settles.\n */\n const isCommandPending = useCallback(\n (nodeId: string, kind?: CommandKind): boolean => {\n if (kind) {\n const prefix = `${nodeId}:${kind}`;\n for (const key of pendingCommands) {\n if (key === prefix || key.startsWith(`${prefix}:`)) return true;\n }\n return false;\n }\n const prefix = `${nodeId}:`;\n for (const key of pendingCommands) {\n if (key.startsWith(prefix)) return true;\n }\n return false;\n },\n [pendingCommands]\n );\n\n // Increment/decrement the per-key in-flight count and publish the reactive set.\n const markPending = useCallback(\n (key: string, delta: 1 | -1) => {\n const next = (pendingRef.current.get(key) ?? 0) + delta;\n if (next > 0) pendingRef.current.set(key, next);\n else pendingRef.current.delete(key);\n syncPending();\n },\n [syncPending]\n );\n\n /**\n * Shared command runner. Every command helper routes through here so they all\n * share one contract:\n * - The HMI never authors device state. `isRunning`, `status`, `currentMode`\n * and parameter values are process truth reconciled from telemetry; a\n * command only *sends* and records its own outcome in `lastCommandResult`.\n * - Commands always dispatch — there is NO guard that blocks or de-dupes a\n * repeat press. The HMI fails open: every invocation sends. (Telemetry is\n * the source of truth, so a duplicate command is reconciled there.)\n * - The command is counted as in-flight only so a caller can show a\n * \"sending…\" hint; this never disables anything. `commandTimeoutMs` only\n * bounds how long that hint shows and records a timeout result — it cannot\n * lock a control out, because controls are never disabled.\n */\n const runCommand = useCallback(\n async (\n nodeId: string,\n key: string,\n invoke: (binding: DeviceControlBinding) => void | Promise<void>,\n messages: { success: string; failure: string }\n ): Promise<void> => {\n const binding = controls.get(nodeId);\n if (!binding) return;\n\n markPending(key, 1);\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const action = Promise.resolve(invoke(binding));\n if (commandTimeoutMs && commandTimeoutMs > 0) {\n await Promise.race([\n action,\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`${messages.success.replace(/ sent$/, '')} timed out (unconfirmed)`)),\n commandTimeoutMs\n );\n }),\n ]);\n } else {\n await action;\n }\n\n updateDeviceState(nodeId, {\n lastCommandTime: Date.now(),\n lastCommandResult: { success: true, message: messages.success },\n });\n } catch (error) {\n updateDeviceState(nodeId, {\n lastCommandTime: Date.now(),\n lastCommandResult: {\n success: false,\n message: error instanceof Error ? error.message : messages.failure,\n },\n });\n } finally {\n if (timer) clearTimeout(timer);\n markPending(key, -1);\n }\n },\n [controls, updateDeviceState, markPending, commandTimeoutMs]\n );\n\n /** Send a mode change command. Displayed mode is reconciled from telemetry. */\n const sendModeChange = useCallback(\n (nodeId: string, mode: string) =>\n runCommand(\n nodeId,\n commandKey(nodeId, 'mode'),\n (binding) => binding.actions.onModeChange?.(mode),\n { success: `Mode ${mode} command sent`, failure: 'Mode change failed' }\n ),\n [runCommand]\n );\n\n /** Send a parameter change command. Displayed value is reconciled from telemetry. */\n const sendParameterChange = useCallback(\n (nodeId: string, parameterId: string, value: any) =>\n runCommand(\n nodeId,\n commandKey(nodeId, 'parameter', parameterId),\n (binding) => binding.actions.onParameterChange?.(parameterId, value),\n { success: `Parameter ${parameterId} command sent`, failure: 'Parameter change failed' }\n ),\n [runCommand]\n );\n\n /** Send a start command. Running state is reconciled from telemetry. */\n const sendStartCommand = useCallback(\n (nodeId: string) =>\n runCommand(nodeId, commandKey(nodeId, 'start'), (binding) => binding.actions.onStart?.(), {\n success: 'Start command sent',\n failure: 'Start command failed',\n }),\n [runCommand]\n );\n\n /** Send a stop command. Running state is reconciled from telemetry. */\n const sendStopCommand = useCallback(\n (nodeId: string) =>\n runCommand(nodeId, commandKey(nodeId, 'stop'), (binding) => binding.actions.onStop?.(), {\n success: 'Stop command sent',\n failure: 'Stop command failed',\n }),\n [runCommand]\n );\n\n /** Send a custom action command. */\n const sendCustomAction = useCallback(\n (nodeId: string, actionId: string) =>\n runCommand(\n nodeId,\n commandKey(nodeId, 'action', actionId),\n (binding) => binding.actions.onCustomAction?.(actionId),\n { success: `Action ${actionId} command sent`, failure: 'Action failed' }\n ),\n [runCommand]\n );\n\n return {\n controls,\n updateControl,\n updateControlBatch,\n updateDeviceState,\n updateParameter,\n setControlBinding,\n removeControlBinding,\n getControlBinding,\n // In-flight command visibility (interaction state, not device state).\n // `isCommandPending` is the stable query surface; the raw key set is\n // intentionally not exported (its key format is an implementation detail).\n isCommandPending,\n // Command helpers\n sendModeChange,\n sendParameterChange,\n sendStartCommand,\n sendStopCommand,\n sendCustomAction,\n };\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { ForcedState } from './TelemetryBinding';\n\n/**\n * Configuration parameter definition\n */\nexport interface ConfigParameter {\n /** Parameter ID */\n id: string;\n /** Display label */\n label: string;\n /** Parameter description/help text */\n description?: string;\n /** Unit of measurement */\n unit?: string;\n /** Parameter type */\n type: 'number' | 'string' | 'boolean' | 'select' | 'multiselect';\n /** Current value */\n value: any;\n /**\n * Maintenance force state for this parameter (#3607, R19). A field the\n * operator has not overridden falls back to control's live value (the\n * pilet's own DashboardScreen does this on every config refresh), so\n * `value` here can be a live, forced reading, not only a static setting.\n * Absent or `active: false` means `value` is a real reading or a real\n * saved setting. See `ForcedState`.\n */\n forced?: ForcedState;\n /** Default value */\n defaultValue?: any;\n /** Whether parameter is read-only */\n readOnly?: boolean;\n /** Whether parameter is enabled/editable */\n enabled?: boolean;\n \n // Numeric constraints\n /** Minimum allowed value (for number type) */\n min?: number;\n /** Maximum allowed value (for number type) */\n max?: number;\n /** Step increment (for number type) */\n step?: number;\n /** Decimal precision for display (for number type) */\n precision?: number;\n \n // Selection options\n /** Options for select/multiselect type */\n options?: Array<{ value: any; label: string; description?: string }>;\n \n // Validation\n /** Custom validation function - returns true or error message */\n validate?: (value: any) => boolean | string;\n /** Required field */\n required?: boolean;\n \n // Visual grouping\n /** Section this parameter belongs to */\n section?: string;\n /** Display order within section */\n order?: number;\n \n // Dependencies\n /** Parameter IDs this depends on - if any are disabled, this is disabled */\n dependsOn?: string[];\n /** Conditional visibility - function that determines if parameter should be shown */\n visibleWhen?: (allValues: Record<string, any>) => boolean;\n}\n\n/**\n * Configuration section for grouping parameters\n */\nexport interface ConfigSection {\n /** Section ID */\n id: string;\n /** Display title */\n title: string;\n /** Section description */\n description?: string;\n /** Display order */\n order?: number;\n /** Whether section is collapsible */\n collapsible?: boolean;\n /** Default collapsed state */\n defaultCollapsed?: boolean;\n /** Icon to display */\n icon?: string;\n}\n\n/**\n * Configuration state tracking\n */\nexport interface ConfigState {\n /** Has unsaved changes */\n isDirty?: boolean;\n /** Currently saving */\n isSaving?: boolean;\n /** Last save timestamp */\n lastSaved?: number;\n /** Last save result */\n lastSaveResult?: {\n success: boolean;\n message?: string;\n };\n /** Validation errors */\n errors?: Record<string, string>;\n}\n\n/**\n * Configuration actions/handlers\n */\nexport interface ConfigActions {\n /** Handler for applying changes (temporary, may be reverted) */\n onApply?: (values: Record<string, any>) => Promise<{ success: boolean; message?: string }>;\n /** Handler for saving changes (permanent) */\n onSave?: (values: Record<string, any>) => Promise<{ success: boolean; message?: string }>;\n /** Handler for resetting to defaults */\n onReset?: () => Promise<{ success: boolean; message?: string }>;\n /** Handler for canceling changes */\n onCancel?: () => void;\n /** Handler for exporting configuration */\n onExport?: () => Promise<{ success: boolean; data?: any; message?: string }>;\n /** Handler for importing configuration */\n onImport?: (data: any) => Promise<{ success: boolean; message?: string }>;\n}\n\n/**\n * Display configuration\n */\nexport interface ConfigDisplay {\n /** Panel title */\n title?: string;\n /** Panel subtitle */\n subtitle?: string;\n /** Custom CSS class */\n className?: string;\n /** Custom inline styles */\n style?: React.CSSProperties;\n /** Show apply button */\n showApply?: boolean;\n /** Show save button */\n showSave?: boolean;\n /** Show reset button */\n showReset?: boolean;\n /** Show cancel button */\n showCancel?: boolean;\n /** Show export button */\n showExport?: boolean;\n /** Show import button */\n showImport?: boolean;\n /** Confirmation message for reset action */\n resetConfirmMessage?: string;\n /** Confirmation message for critical saves */\n saveConfirmMessage?: string;\n}\n\n/**\n * Theme customization for config panel\n */\nexport interface ConfigTheme {\n /** Background color */\n backgroundColor?: string;\n /** Surface/card color */\n surfaceColor?: string;\n /** Border color */\n borderColor?: string;\n /** Text color */\n textColor?: string;\n /** Muted text color */\n mutedTextColor?: string;\n /** Accent color */\n accentColor?: string;\n /** Error color */\n errorColor?: string;\n /** Success color */\n successColor?: string;\n /** Warning color */\n warningColor?: string;\n}\n\n/**\n * Device configuration binding - maps a node to device configuration parameters\n */\nexport interface DeviceConfigBinding {\n /** Node ID this configuration is bound to */\n nodeId: string;\n /** Device tag/identifier */\n tag: string;\n /** Device name */\n deviceName?: string;\n /** Device type */\n deviceType?: string;\n \n /** Configuration sections */\n sections?: ConfigSection[];\n /** Configuration parameters (flat list, optionally grouped by section) */\n parameters: ConfigParameter[];\n \n /** Current state */\n state: ConfigState;\n /** Available actions */\n actions: ConfigActions;\n /** Display configuration */\n display?: ConfigDisplay;\n /** Theme customization */\n theme?: ConfigTheme;\n \n /** Additional metadata */\n metadata?: Record<string, any>;\n}\n\n/**\n * Map of device configurations by node ID\n */\nexport type DeviceConfigMap = Map<string, DeviceConfigBinding>;\n\n/**\n * Helper to create a config binding with defaults\n */\nexport function createConfigBinding(\n nodeId: string,\n tag: string,\n parameters: ConfigParameter[],\n actions: ConfigActions,\n options?: {\n deviceName?: string;\n deviceType?: string;\n sections?: ConfigSection[];\n display?: ConfigDisplay;\n theme?: ConfigTheme;\n metadata?: Record<string, any>;\n }\n): DeviceConfigBinding {\n return {\n nodeId,\n tag,\n deviceName: options?.deviceName,\n deviceType: options?.deviceType,\n sections: options?.sections,\n parameters,\n state: {\n isDirty: false,\n isSaving: false,\n errors: {},\n },\n actions,\n display: options?.display,\n theme: options?.theme,\n metadata: options?.metadata,\n };\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { useState, useCallback } from 'react';\nimport type { DeviceConfigBinding, DeviceConfigMap } from '../diagram/schema/DeviceConfigBinding';\n\n/**\n * Hook for managing device configuration bindings\n */\nexport function useDeviceConfigs(initialConfigs?: DeviceConfigBinding[]) {\n const [configs, setConfigs] = useState<DeviceConfigMap>(() => {\n const map = new Map();\n if (initialConfigs) {\n initialConfigs.forEach((config) => {\n map.set(config.nodeId, config);\n });\n }\n return map;\n });\n\n /**\n * Get configuration for a specific device\n */\n const getConfig = useCallback(\n (nodeId: string): DeviceConfigBinding | undefined => {\n return configs.get(nodeId);\n },\n [configs]\n );\n\n /**\n * Update a specific configuration binding\n */\n const updateConfig = useCallback((nodeId: string, updates: Partial<DeviceConfigBinding>) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n\n if (existing) {\n newMap.set(nodeId, { ...existing, ...updates });\n } else {\n // If binding doesn't exist, create new one with required fields\n if ('tag' in updates && 'parameters' in updates && 'state' in updates && 'actions' in updates) {\n newMap.set(nodeId, { nodeId, ...updates } as DeviceConfigBinding);\n }\n }\n\n return newMap;\n });\n }, []);\n\n /**\n * Update configuration state\n */\n const updateConfigState = useCallback((nodeId: string, stateUpdates: Partial<DeviceConfigBinding['state']>) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n\n if (existing) {\n newMap.set(nodeId, {\n ...existing,\n state: { ...existing.state, ...stateUpdates },\n });\n }\n\n return newMap;\n });\n }, []);\n\n /**\n * Update multiple configurations at once\n */\n const updateConfigBatch = useCallback((updates: Record<string, Partial<DeviceConfigBinding>>) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n\n Object.entries(updates).forEach(([nodeId, update]) => {\n const existing = newMap.get(nodeId);\n if (existing) {\n newMap.set(nodeId, { ...existing, ...update });\n }\n });\n\n return newMap;\n });\n }, []);\n\n /**\n * Update parameter value\n */\n const updateParameterValue = useCallback((nodeId: string, parameterId: string, value: any) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n\n if (existing) {\n const updatedParameters = existing.parameters.map((param) =>\n param.id === parameterId ? { ...param, value } : param\n );\n\n newMap.set(nodeId, {\n ...existing,\n parameters: updatedParameters,\n state: { ...existing.state, isDirty: true },\n });\n }\n\n return newMap;\n });\n }, []);\n\n /**\n * Apply configuration changes\n */\n const applyConfig = useCallback(\n async (nodeId: string): Promise<{ success: boolean; message?: string }> => {\n const config = configs.get(nodeId);\n if (!config || !config.actions.onApply) {\n return { success: false, message: 'No apply handler defined' };\n }\n\n // Set saving state\n updateConfigState(nodeId, { isSaving: true });\n\n try {\n // Get current values\n const values = config.parameters.reduce(\n (acc, param) => {\n acc[param.id] = param.value;\n return acc;\n },\n {} as Record<string, any>\n );\n\n // Call apply handler\n const result = await config.actions.onApply(values);\n\n // Update state with result\n updateConfigState(nodeId, {\n isSaving: false,\n lastSaveResult: result,\n isDirty: result.success ? false : config.state.isDirty,\n });\n\n return result;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Apply failed';\n updateConfigState(nodeId, {\n isSaving: false,\n lastSaveResult: { success: false, message },\n });\n return { success: false, message };\n }\n },\n [configs, updateConfigState]\n );\n\n /**\n * Save configuration changes\n */\n const saveConfig = useCallback(\n async (nodeId: string): Promise<{ success: boolean; message?: string }> => {\n const config = configs.get(nodeId);\n if (!config || !config.actions.onSave) {\n return { success: false, message: 'No save handler defined' };\n }\n\n // Set saving state\n updateConfigState(nodeId, { isSaving: true });\n\n try {\n // Get current values\n const values = config.parameters.reduce(\n (acc, param) => {\n acc[param.id] = param.value;\n return acc;\n },\n {} as Record<string, any>\n );\n\n // Call save handler\n const result = await config.actions.onSave(values);\n\n // Update state with result\n updateConfigState(nodeId, {\n isSaving: false,\n lastSaved: result.success ? Date.now() : config.state.lastSaved,\n lastSaveResult: result,\n isDirty: result.success ? false : config.state.isDirty,\n });\n\n return result;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Save failed';\n updateConfigState(nodeId, {\n isSaving: false,\n lastSaveResult: { success: false, message },\n });\n return { success: false, message };\n }\n },\n [configs, updateConfigState]\n );\n\n /**\n * Reset configuration to defaults\n */\n const resetConfig = useCallback(\n async (nodeId: string): Promise<{ success: boolean; message?: string }> => {\n const config = configs.get(nodeId);\n if (!config) {\n return { success: false, message: 'Configuration not found' };\n }\n\n try {\n // Reset to default values\n const resetParameters = config.parameters.map((param) => ({\n ...param,\n value: param.defaultValue !== undefined ? param.defaultValue : param.value,\n }));\n\n updateConfig(nodeId, {\n parameters: resetParameters,\n state: { ...config.state, isDirty: true },\n });\n\n // Call reset handler if defined\n if (config.actions.onReset) {\n const result = await config.actions.onReset();\n updateConfigState(nodeId, {\n lastSaveResult: result,\n });\n return result;\n }\n\n return { success: true, message: 'Reset to defaults' };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Reset failed';\n return { success: false, message };\n }\n },\n [configs, updateConfig, updateConfigState]\n );\n\n /**\n * Cancel configuration changes\n */\n const cancelConfig = useCallback(\n (nodeId: string) => {\n const config = configs.get(nodeId);\n if (config?.actions.onCancel) {\n config.actions.onCancel();\n }\n },\n [configs]\n );\n\n /**\n * Validate all parameters\n */\n const validateConfig = useCallback(\n (nodeId: string): { valid: boolean; errors: Record<string, string> } => {\n const config = configs.get(nodeId);\n if (!config) {\n return { valid: false, errors: { _general: 'Configuration not found' } };\n }\n\n const errors: Record<string, string> = {};\n const values = config.parameters.reduce(\n (acc, param) => {\n acc[param.id] = param.value;\n return acc;\n },\n {} as Record<string, any>\n );\n\n config.parameters.forEach((param) => {\n // Check required\n if (param.required && (param.value === undefined || param.value === null || param.value === '')) {\n errors[param.id] = `${param.label} is required`;\n return;\n }\n\n // Check numeric constraints\n if (param.type === 'number' && typeof param.value === 'number') {\n if (param.min !== undefined && param.value < param.min) {\n errors[param.id] = `${param.label} must be at least ${param.min}`;\n }\n if (param.max !== undefined && param.value > param.max) {\n errors[param.id] = `${param.label} must be at most ${param.max}`;\n }\n }\n\n // Check custom validation\n if (param.validate) {\n const result = param.validate(param.value);\n if (result !== true) {\n errors[param.id] = typeof result === 'string' ? result : `${param.label} is invalid`;\n }\n }\n\n // Check visibility\n if (param.visibleWhen && !param.visibleWhen(values)) {\n // Skip validation for hidden parameters\n delete errors[param.id];\n }\n });\n\n // Update state with errors\n updateConfigState(nodeId, { errors });\n\n return { valid: Object.keys(errors).length === 0, errors };\n },\n [configs, updateConfigState]\n );\n\n /**\n * Set configuration for a device (replaces existing)\n */\n const setConfig = useCallback((config: DeviceConfigBinding) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n newMap.set(config.nodeId, config);\n return newMap;\n });\n }, []);\n\n /**\n * Remove configuration for a device\n */\n const removeConfig = useCallback((nodeId: string) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n newMap.delete(nodeId);\n return newMap;\n });\n }, []);\n\n /**\n * Clear all configurations\n */\n const clearConfigs = useCallback(() => {\n setConfigs(new Map());\n }, []);\n\n return {\n configs,\n getConfig,\n setConfig,\n removeConfig,\n clearConfigs,\n updateConfig,\n updateConfigState,\n updateConfigBatch,\n updateParameterValue,\n applyConfig,\n saveConfig,\n resetConfig,\n cancelConfig,\n validateConfig,\n };\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport React, { useState, useEffect, useMemo, useRef } from 'react';\nimport type {\n DeviceConfigBinding,\n ConfigParameter,\n ConfigSection,\n} from '../../diagram/schema/DeviceConfigBinding';\nimport { FORCED_MARK_COLOR } from '../../theme/statusColors';\n\nexport interface DeviceConfigPanelProps {\n /** Configuration binding to display */\n binding: DeviceConfigBinding | null;\n /** Called when panel should close */\n onClose: () => void;\n /** Called when undock button clicked (optional, shows undock button if provided) */\n onUndock?: () => void;\n /** Icon to display in undock button (default: '⇱') */\n undockIcon?: string;\n /** Panel position */\n position?: 'bottom' | 'side';\n /** Horizontal alignment for bottom position (default: center) */\n align?: 'left' | 'center' | 'right';\n /** Optimize for touch (larger targets) */\n touchOptimized?: boolean;\n /** Compact mode - reduces padding and spacing */\n compact?: boolean;\n /** Font size scale (default: 1.0) */\n fontScale?: number;\n /** Maximum height for bottom position (default: 70vh) */\n maxHeight?: string;\n /** Maximum width for bottom position (default: none) */\n maxWidth?: string;\n /** Toast notification duration in milliseconds (default: 3000) */\n toastDuration?: number;\n /** Called when controls button clicked (opens control panel for this device) */\n onOpenControls?: () => void;\n /** Show controls button in header */\n showControlsButton?: boolean;\n /**\n * Render this panel's OWN result toast. Default true, so existing\n * consumers are unchanged.\n *\n * Set false when the host application already surfaces command and save\n * results through its own notification channel. Without it the operator\n * sees two notices for one action - this panel's optimistic \"command\n * sent\", in this panel's styling, followed by the host's message for the\n * real outcome - which reads as two different mechanisms.\n *\n * The result still lands in `binding.state`, so a host that opts out can\n * read it and render its own.\n */\n showBuiltInToast?: boolean;\n /** Embedded mode - uses static positioning for rendering in containers (default: false) */\n embedded?: boolean;\n}\n\n/**\n * Memoized below (kiosk heap-leak fix): a host whose screen re-renders ~1Hz on\n * telemetry must not re-render an OPEN config panel every tick - each pass\n * re-creates the panel's handler closures and re-binds its MUI event handlers,\n * and on the kiosk those generations were RETAINED (~100 closures/sec,\n * ~2.1 MB/min heap growth -> renderer OOM at the 512 MB cap, heap-snapshot\n * fingerprint: Context/scope buckets + onSave/onReset closures). The memo only\n * pays off when the host passes identity-stable `binding` and handler props;\n * an unstable host degrades gracefully to the old behavior.\n */\nfunction DeviceConfigPanelImpl({\n binding,\n onClose,\n onUndock,\n undockIcon = '⇱',\n position = 'bottom',\n align = 'center',\n touchOptimized = true,\n compact = false,\n fontScale = 1.0,\n maxHeight,\n maxWidth,\n toastDuration = 3000,\n onOpenControls,\n showControlsButton = true,\n showBuiltInToast = true,\n embedded = false,\n}: DeviceConfigPanelProps) {\n const [localValues, setLocalValues] = useState<Record<string, any>>(() => {\n if (binding) {\n const values: Record<string, any> = {};\n binding.parameters.forEach((param) => {\n values[param.id] = param.value;\n });\n return values;\n }\n return {};\n });\n const [collapsedSections, setCollapsedSections] = useState<Set<string>>(() => {\n const collapsed = new Set<string>();\n if (binding?.sections) {\n binding.sections.forEach((section) => {\n if (section.collapsible && section.defaultCollapsed) {\n collapsed.add(section.id);\n }\n });\n }\n return collapsed;\n });\n const [showToast, setShowToast] = useState(true);\n const [bindingId, setBindingId] = useState<string | null>(binding?.nodeId || null);\n const lastResultTimestampRef = useRef<number>(0);\n\n // Reset local values when binding changes (new device selected)\n if (binding && binding.nodeId !== bindingId) {\n const values: Record<string, any> = {};\n binding.parameters.forEach((param) => {\n values[param.id] = param.value;\n });\n setLocalValues(values);\n\n // Reset collapsed sections for new binding\n const collapsed = new Set<string>();\n binding.sections?.forEach((section) => {\n if (section.collapsible && section.defaultCollapsed) {\n collapsed.add(section.id);\n }\n });\n setCollapsedSections(collapsed);\n\n setBindingId(binding.nodeId);\n }\n\n // Auto-dismiss toast - defer setState to avoid cascading renders\n useEffect(() => {\n const currentTimestamp = binding?.state.lastSaved || 0;\n if (binding?.state.lastSaveResult && currentTimestamp !== lastResultTimestampRef.current) {\n lastResultTimestampRef.current = currentTimestamp;\n\n // Defer setState to next tick to avoid cascading renders.\n // hideTimer is hoisted so the effect cleanup can clear BOTH timers — the\n // inner `return` from the setTimeout callback was discarded (setTimeout\n // ignores return values), leaving hideTimer dangling on unmount.\n let hideTimer: ReturnType<typeof setTimeout> | undefined;\n const showTimer = setTimeout(() => {\n setShowToast(true);\n hideTimer = setTimeout(() => {\n setShowToast(false);\n }, toastDuration);\n }, 0);\n\n return () => {\n clearTimeout(showTimer);\n if (hideTimer) clearTimeout(hideTimer);\n };\n }\n }, [binding?.state.lastSaveResult, binding?.state.lastSaved, toastDuration]);\n\n // Group parameters by section\n const parametersBySection = useMemo(() => {\n if (!binding) return { _unsectioned: [] };\n\n const groups: Record<string, ConfigParameter[]> = { _unsectioned: [] };\n\n binding.parameters.forEach((param) => {\n const sectionId = param.section || '_unsectioned';\n if (!groups[sectionId]) {\n groups[sectionId] = [];\n }\n groups[sectionId].push(param);\n });\n\n // Sort parameters within each section by order\n Object.keys(groups).forEach((sectionId) => {\n groups[sectionId].sort((a, b) => (a.order || 0) - (b.order || 0));\n });\n\n return groups;\n }, [binding]);\n\n // Get sections sorted by order\n const sortedSections = useMemo(() => {\n if (!binding?.sections) return [];\n return [...binding.sections].sort((a, b) => (a.order || 0) - (b.order || 0));\n }, [binding]);\n\n if (!binding) return null;\n\n const touchSize = touchOptimized ? 56 : 40;\n\n // Spacing\n const spacing = {\n padding: compact ? '12px' : '20px',\n gap: compact ? '6px' : '8px',\n marginBottom: compact ? '10px' : '16px',\n itemPadding: compact ? '8px 10px' : '10px 12px',\n };\n\n // Font sizes\n const fontSize = {\n title: Math.round(14 * fontScale),\n label: Math.round(11 * fontScale),\n value: Math.round(13 * fontScale),\n input: Math.round(13 * fontScale),\n button: Math.round(12 * fontScale),\n section: Math.round(12 * fontScale),\n description: Math.round(10 * fontScale),\n };\n\n // ISA-101 Colors\n const backgroundColor = binding.theme?.backgroundColor || '#edeeef';\n const surfaceColor = binding.theme?.surfaceColor || '#f5f6f7';\n const borderColor = binding.theme?.borderColor || '#d2d2d2';\n const textColor = binding.theme?.textColor || '#000000';\n const mutedTextColor = binding.theme?.mutedTextColor || '#666666';\n const accentColor = binding.theme?.accentColor || '#b4d0fe';\n const errorColor = binding.theme?.errorColor || '#ef4444';\n const successColor = binding.theme?.successColor || '#10b981';\n\n const handleValueChange = (parameterId: string, value: any) => {\n setLocalValues((prev) => ({ ...prev, [parameterId]: value }));\n };\n\n const handleApply = async () => {\n if (binding.actions.onApply) {\n await binding.actions.onApply(localValues);\n }\n };\n\n const handleSave = async () => {\n if (binding.actions.onSave) {\n await binding.actions.onSave(localValues);\n }\n };\n\n const handleReset = async () => {\n if (binding.display?.resetConfirmMessage) {\n if (!confirm(binding.display.resetConfirmMessage)) {\n return;\n }\n }\n if (binding.actions.onReset) {\n await binding.actions.onReset();\n }\n };\n\n const handleCancel = () => {\n if (binding.actions.onCancel) {\n binding.actions.onCancel();\n }\n onClose();\n };\n\n const toggleSection = (sectionId: string) => {\n setCollapsedSections((prev) => {\n const next = new Set(prev);\n if (next.has(sectionId)) {\n next.delete(sectionId);\n } else {\n next.add(sectionId);\n }\n return next;\n });\n };\n\n const renderParameter = (param: ConfigParameter) => {\n // Check visibility\n if (param.visibleWhen && !param.visibleWhen(localValues)) {\n return null;\n }\n\n const value = localValues[param.id];\n const error = binding.state.errors?.[param.id];\n const isDisabled = param.readOnly || param.enabled === false;\n\n return (\n <div\n key={param.id}\n style={{\n marginBottom: spacing.marginBottom,\n }}\n >\n {/* Label */}\n <label\n style={{\n display: 'block',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n color: textColor,\n marginBottom: '4px',\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n {param.label}\n {param.required && <span style={{ color: errorColor }}> *</span>}\n {param.unit && (\n <span style={{ color: mutedTextColor, fontWeight: 'normal' }}> ({param.unit})</span>\n )}\n {param.forced?.active && (\n <span\n aria-label=\"Forced\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '16px',\n height: '16px',\n marginLeft: '6px',\n fontSize: '10px',\n fontWeight: 700,\n textTransform: 'none',\n letterSpacing: 'normal',\n color: FORCED_MARK_COLOR.text,\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n borderRadius: '50%',\n verticalAlign: 'middle',\n }}\n >\n F\n </span>\n )}\n </label>\n\n {/* Real (unforced) value (#3607, R19). A parameter the operator has\n not overridden falls back to control's live value, so it can be\n a forced reading here just as much as on the control panel. */}\n {param.forced?.active && param.forced.realValue !== undefined && (\n <div\n style={{\n fontSize: `${fontSize.description}px`,\n color: mutedTextColor,\n marginTop: '-2px',\n marginBottom: '6px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n REAL{' '}\n {typeof param.forced.realValue === 'boolean'\n ? param.forced.realValue\n ? 'ON'\n : 'OFF'\n : String(param.forced.realValue)}\n {param.unit ? ` ${param.unit}` : ''}\n </div>\n )}\n\n {/* Description */}\n {param.description && (\n <div\n style={{\n fontSize: `${fontSize.description}px`,\n color: mutedTextColor,\n marginBottom: '6px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {param.description}\n </div>\n )}\n\n {/* Input */}\n {param.type === 'number' && (\n <input\n type=\"number\"\n value={value ?? ''}\n onChange={(e) => handleValueChange(param.id, parseFloat(e.target.value))}\n min={param.min}\n max={param.max}\n step={param.step}\n disabled={isDisabled}\n style={{\n width: '100%',\n height: `${touchSize * 0.8}px`,\n padding: spacing.itemPadding,\n fontSize: `${fontSize.input}px`,\n fontFamily: 'Arial, sans-serif',\n backgroundColor: isDisabled ? '#e5e7eb' : surfaceColor,\n color: textColor,\n border: `1px solid ${error ? errorColor : borderColor}`,\n borderRadius: '2px',\n outline: 'none',\n }}\n />\n )}\n\n {param.type === 'string' && (\n <input\n type=\"text\"\n value={value ?? ''}\n onChange={(e) => handleValueChange(param.id, e.target.value)}\n disabled={isDisabled}\n style={{\n width: '100%',\n height: `${touchSize * 0.8}px`,\n padding: spacing.itemPadding,\n fontSize: `${fontSize.input}px`,\n fontFamily: 'Arial, sans-serif',\n backgroundColor: isDisabled ? '#e5e7eb' : surfaceColor,\n color: textColor,\n border: `1px solid ${error ? errorColor : borderColor}`,\n borderRadius: '2px',\n outline: 'none',\n }}\n />\n )}\n\n {param.type === 'boolean' && (\n <label\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n cursor: isDisabled ? 'not-allowed' : 'pointer',\n }}\n >\n <input\n type=\"checkbox\"\n checked={value ?? false}\n onChange={(e) => handleValueChange(param.id, e.target.checked)}\n disabled={isDisabled}\n style={{\n width: '20px',\n height: '20px',\n cursor: isDisabled ? 'not-allowed' : 'pointer',\n }}\n />\n <span style={{ fontSize: `${fontSize.value}px`, color: textColor }}>\n {value ? 'Enabled' : 'Disabled'}\n </span>\n </label>\n )}\n\n {param.type === 'select' && (\n <select\n value={value ?? ''}\n onChange={(e) => handleValueChange(param.id, e.target.value)}\n disabled={isDisabled}\n style={{\n width: '100%',\n height: `${touchSize * 0.8}px`,\n padding: spacing.itemPadding,\n fontSize: `${fontSize.input}px`,\n fontFamily: 'Arial, sans-serif',\n backgroundColor: isDisabled ? '#e5e7eb' : surfaceColor,\n color: textColor,\n border: `1px solid ${error ? errorColor : borderColor}`,\n borderRadius: '2px',\n outline: 'none',\n }}\n >\n {param.options?.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n )}\n\n {/* Error message */}\n {error && (\n <div\n style={{\n fontSize: `${fontSize.description}px`,\n color: errorColor,\n marginTop: '4px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {error}\n </div>\n )}\n </div>\n );\n };\n\n const renderSection = (section: ConfigSection) => {\n const params = parametersBySection[section.id] || [];\n const isCollapsed = collapsedSections.has(section.id);\n\n return (\n <div\n key={section.id}\n style={{\n marginBottom: spacing.marginBottom,\n border: `1px solid ${borderColor}`,\n borderRadius: '4px',\n backgroundColor: surfaceColor,\n }}\n >\n {/* Section header */}\n <div\n onClick={() => section.collapsible && toggleSection(section.id)}\n style={{\n padding: spacing.itemPadding,\n borderBottom: isCollapsed ? 'none' : `1px solid ${borderColor}`,\n cursor: section.collapsible ? 'pointer' : 'default',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <div>\n <div\n style={{\n fontSize: `${fontSize.section}px`,\n fontWeight: 600,\n color: textColor,\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n {section.icon && <span style={{ marginRight: '8px' }}>{section.icon}</span>}\n {section.title}\n </div>\n {section.description && (\n <div\n style={{\n fontSize: `${fontSize.description}px`,\n color: mutedTextColor,\n marginTop: '4px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {section.description}\n </div>\n )}\n </div>\n {section.collapsible && (\n <span style={{ fontSize: '16px', color: mutedTextColor }}>\n {isCollapsed ? '▼' : '▲'}\n </span>\n )}\n </div>\n\n {/* Section content */}\n {!isCollapsed && (\n <div style={{ padding: spacing.padding }}>{params.map(renderParameter)}</div>\n )}\n </div>\n );\n };\n\n // Panel styles\n const panelStyle: React.CSSProperties = embedded\n ? {\n // Embedded mode: static positioning for container layouts\n position: 'static',\n width: '100%',\n backgroundColor,\n color: textColor,\n borderRadius: '8px 8px 0 0',\n boxShadow: 'none',\n border: `2px solid ${borderColor}`,\n padding: spacing.padding,\n maxHeight: maxHeight || '70vh',\n overflowY: 'auto',\n display: 'flex',\n flexDirection: 'column',\n ...binding.display?.style,\n }\n : {\n // Fixed mode: traditional overlay positioning\n position: 'fixed',\n ...(position === 'bottom'\n ? {\n bottom: 0,\n ...(align === 'left'\n ? { left: 0, right: 'auto', width: maxWidth }\n : align === 'right'\n ? { right: 0, left: 'auto', width: maxWidth }\n : {\n left: 0,\n right: 0,\n marginLeft: 'auto',\n marginRight: 'auto',\n maxWidth: maxWidth,\n }),\n animation: 'slideUp 0.3s ease-out',\n }\n : {\n right: 0,\n top: 0,\n bottom: 0,\n width: maxWidth || '400px',\n animation: 'slideIn 0.3s ease-out',\n }),\n backgroundColor,\n color: textColor,\n borderRadius: position === 'bottom' ? '8px 8px 0 0' : '8px 0 0 8px',\n boxShadow: 'none',\n border: `2px solid ${borderColor}`,\n padding: spacing.padding,\n zIndex: 10000,\n maxHeight: maxHeight || (position === 'bottom' ? '70vh' : '100vh'),\n overflowY: 'auto',\n display: 'flex',\n flexDirection: 'column',\n ...binding.display?.style,\n };\n\n const titleText = binding.display?.title || binding.deviceName || binding.tag || binding.nodeId;\n const showApply = binding.display?.showApply !== false && binding.actions.onApply;\n const showSave = binding.display?.showSave !== false && binding.actions.onSave;\n const showReset = binding.display?.showReset !== false && binding.actions.onReset;\n const showCancel = binding.display?.showCancel !== false;\n\n return (\n <>\n {/* Panel */}\n <div style={panelStyle} className={binding.display?.className}>\n {/* Header */}\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: '16px',\n paddingBottom: '12px',\n borderBottom: `2px solid ${borderColor}`,\n }}\n >\n <div>\n <h3\n style={{\n margin: 0,\n fontSize: `${fontSize.title}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n color: textColor,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n maxWidth: '200px',\n }}\n title={titleText.length > 25 ? titleText : undefined}\n >\n {titleText.length > 25 ? titleText.slice(0, 25) + '...' : titleText}\n </h3>\n {binding.display?.subtitle && (\n <div\n style={{\n fontSize: `${fontSize.description}px`,\n color: mutedTextColor,\n marginTop: '4px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {binding.display.subtitle}\n </div>\n )}\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n {/* Controls button */}\n {showControlsButton && onOpenControls && (\n <button\n onClick={onOpenControls}\n title=\"Open Controls\"\n style={{\n minWidth: `${touchSize * 1.2}px`,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: `${fontSize.button}px`,\n cursor: 'pointer',\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n padding: '0 8px',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n CTRL\n </button>\n )}\n {/* Undock button */}\n {onUndock && (\n <button\n onClick={onUndock}\n title=\"Undock panel\"\n style={{\n width: touchSize * 0.7,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: '16px',\n cursor: 'pointer',\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n padding: '0',\n }}\n >\n {undockIcon}\n </button>\n )}\n {/* Close button */}\n <button\n onClick={onClose}\n style={{\n width: touchSize * 0.7,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: '18px',\n cursor: 'pointer',\n fontWeight: 'bold',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n ×\n </button>\n </div>\n </div>\n\n {/* Dirty indicator */}\n {binding.state.isDirty && (\n <div\n style={{\n padding: spacing.itemPadding,\n backgroundColor: '#fff3cd',\n border: '1px solid #ffc107',\n borderRadius: '4px',\n marginBottom: spacing.marginBottom,\n fontSize: `${fontSize.description}px`,\n fontFamily: 'Arial, sans-serif',\n }}\n >\n Unsaved changes\n </div>\n )}\n\n {/* Content - scrollable */}\n <div style={{ flex: 1, overflowY: 'auto', marginBottom: spacing.marginBottom }}>\n {/* Render sections */}\n {sortedSections.length > 0 ? (\n sortedSections.map(renderSection)\n ) : (\n /* Render unsectioned parameters */\n <div>{parametersBySection._unsectioned?.map(renderParameter)}</div>\n )}\n </div>\n\n {/* Action buttons */}\n <div\n style={{\n display: 'flex',\n gap: spacing.gap,\n paddingTop: '12px',\n borderTop: `2px solid ${borderColor}`,\n flexWrap: 'wrap',\n }}\n >\n {showApply && (\n <button\n onClick={handleApply}\n disabled={binding.state.isSaving}\n style={{\n flex: 1,\n minWidth: '80px',\n height: `${touchSize}px`,\n backgroundColor: accentColor,\n color: '#000000',\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n cursor: binding.state.isSaving ? 'not-allowed' : 'pointer',\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n {binding.state.isSaving ? 'Applying...' : 'Apply'}\n </button>\n )}\n\n {showSave && (\n <button\n onClick={handleSave}\n disabled={binding.state.isSaving}\n style={{\n flex: 1,\n minWidth: '80px',\n height: `${touchSize}px`,\n backgroundColor: successColor,\n color: '#ffffff',\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n cursor: binding.state.isSaving ? 'not-allowed' : 'pointer',\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n {binding.state.isSaving ? 'Saving...' : 'Save'}\n </button>\n )}\n\n {showReset && (\n <button\n onClick={handleReset}\n disabled={binding.state.isSaving}\n style={{\n flex: 1,\n minWidth: '80px',\n height: `${touchSize}px`,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n cursor: binding.state.isSaving ? 'not-allowed' : 'pointer',\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n Reset\n </button>\n )}\n\n {showCancel && (\n <button\n onClick={handleCancel}\n disabled={binding.state.isSaving}\n style={{\n flex: 1,\n minWidth: '80px',\n height: `${touchSize}px`,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n cursor: binding.state.isSaving ? 'not-allowed' : 'pointer',\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n Cancel\n </button>\n )}\n </div>\n </div>\n\n {/* Toast notification */}\n {binding.state.lastSaveResult && showToast && showBuiltInToast && (\n <div\n style={{\n position: 'fixed',\n bottom: '20px',\n ...(align === 'right' ? { left: '20px' } : { right: '20px' }),\n backgroundColor: binding.state.lastSaveResult.success ? successColor : errorColor,\n color: '#ffffff',\n padding: '12px 20px',\n borderRadius: '4px',\n boxShadow: '0 4px 6px rgba(0,0,0,0.1)',\n zIndex: 10000,\n fontSize: `${fontSize.value}px`,\n fontFamily: 'Arial, sans-serif',\n fontWeight: 600,\n }}\n >\n {binding.state.lastSaveResult.message ||\n (binding.state.lastSaveResult.success ? 'Success' : 'Error')}\n </div>\n )}\n </>\n );\n}\n\n/** Memo boundary - see DeviceConfigPanelImpl doc comment (kiosk heap-leak fix). */\nexport const DeviceConfigPanel = React.memo(DeviceConfigPanelImpl);\n","import { useEffect } from 'react';\nimport { calculateThresholdStatus } from '../../utils/thresholds';\nimport type { TelemetryMap, TelemetryBinding } from './useTelemetry';\n\n/**\n * Options for simulation behavior\n */\nexport interface SimulationOptions {\n /** Update interval in milliseconds (default: 2000) */\n interval?: number;\n /** Whether simulation is enabled (default: true) */\n enabled?: boolean;\n /** Maximum change per update (default: 10) */\n maxChange?: number;\n /** Minimum value clamp (default: 0) */\n minValue?: number;\n /** Maximum value clamp (default: 100) */\n maxValue?: number;\n /** Maximum history length (default: 10) */\n historyLength?: number;\n /** Custom value generator function */\n generateValue?: (currentValue: number, nodeId: string) => number;\n}\n\n/**\n * Hook to automatically simulate telemetry data updates\n * Randomly updates numeric telemetry values and calculates status based on thresholds\n * \n * @param telemetry - Telemetry map from useTelemetry\n * @param updateTelemetryBatch - Batch update function from useTelemetry\n * @param options - Simulation configuration options\n * \n * @example\n * ```tsx\n * const { telemetry, updateTelemetryBatch } = useTelemetry();\n * useSimulation(telemetry, updateTelemetryBatch, {\n * interval: 2000,\n * enabled: mode === 'runtime'\n * });\n * ```\n */\nexport function useSimulation(\n telemetry: TelemetryMap,\n updateTelemetryBatch: (updates: Array<any>) => void,\n options: SimulationOptions = {}\n) {\n const {\n interval = 2000,\n enabled = true,\n maxChange = 10,\n minValue = 0,\n maxValue = 100,\n historyLength = 10,\n generateValue,\n } = options;\n\n useEffect(() => {\n if (!enabled) return;\n\n const intervalId = setInterval(() => {\n const updates: Array<{\n nodeId: string;\n tag?: string;\n value?: any;\n history?: number[];\n display?: any;\n thresholds?: any;\n statusColors?: any;\n }> = [];\n\n telemetry.forEach((binding: TelemetryBinding, nodeId: string) => {\n // Only simulate numeric values\n if (typeof binding.value.value !== 'number') return;\n\n const currentValue = binding.value.value as number;\n\n // Generate new value\n let newValue: number;\n if (generateValue) {\n newValue = generateValue(currentValue, nodeId);\n } else {\n // Default random walk simulation\n const change = (Math.random() - 0.5) * maxChange;\n newValue = Math.max(minValue, Math.min(maxValue, currentValue + change));\n }\n\n // Calculate status based on thresholds\n const status = calculateThresholdStatus(newValue, binding.thresholds);\n\n // Update history\n const newHistory = [...(binding.history || []), newValue].slice(-historyLength);\n\n updates.push({\n nodeId,\n tag: binding.tag,\n value: {\n ...binding.value,\n value: newValue,\n status,\n timestamp: Date.now(),\n },\n history: newHistory,\n display: binding.display,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n });\n });\n\n if (updates.length > 0) {\n updateTelemetryBatch(updates);\n }\n }, interval);\n\n return () => clearInterval(intervalId);\n }, [\n enabled,\n interval,\n maxChange,\n minValue,\n maxValue,\n historyLength,\n generateValue,\n telemetry,\n updateTelemetryBatch,\n ]);\n}\n"],"mappings":"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","names":["useState","jsx","selectedOption","useState","jsx","jsxs","useState","useCallback","jsx","jsxs","useState","useCallback","jsx","jsxs","useState","React","jsx","jsxs","jsx","jsxs","React","jsx","jsxs","React","useState","useEffect","Fragment","jsx","jsxs","useState","useEffect","currentParams","React","useState","useEffect","useCallback","useRef","jsx","jsxs","React","jsx","useEffect","useState","Box","styled","jsx","Fragment","jsx","jsxs","Fragment","jsx","jsxs","React","useState","useEffect","jsx","jsx","jsxs","React","useCallback","useState","useEffect","useRef","React","value","jsx","jsxs","nodes","React","React","Fragment","jsx","jsxs","React","Fragment","jsx","jsxs","React","useState","useEffect","jsx","jsxs","useState","useCallback","useRef","useEffect","useState","useCallback","useState","useCallback","useEffect","useRef","useEffect","useEffect","useEffect","useState","useCallback","useRef","useEffect","useEffect","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","nodes","React","useState","jsx","jsxs","useState","Fragment","jsx","jsxs","useState","useCallback","useRef","prefix","useState","useCallback","React","useState","useEffect","useRef","Fragment","jsx","jsxs","useState","useRef","useEffect","React","useEffect","useEffect"]}
1
+ {"version":3,"sources":["../src/components/Button/Button.tsx","../src/components/ControlPanel/ControlPanel.tsx","../src/components/Selector/Selector.tsx","../src/components/ValueEntry/ValueEntry.tsx","../src/components/ValueEntry/LegacyValueEntry.tsx","../src/theme/statusColors.ts","../src/components/StatusIndicator/StatusIndicator.tsx","../src/theme/unknownStatusGlyph.tsx","../src/components/NodeControlsPanel/NodeControlsPanel.tsx","../src/components/DeviceControlPanel/DeviceControlPanel.tsx","../src/components/NumpadDialog/NumpadDialog.tsx","../src/components/layout/FullscreenWorkspace/FullscreenWorkspace.tsx","../src/components/layout/FullscreenWorkspace/FullscreenWorkspace.styles.tsx","../src/components/layout/FullscreenWorkspace/FullscreenWorkspace.overlays.tsx","../src/components/layout/FullscreenWorkspace/FullscreenWorkspace.visuals.tsx","../src/theme/ThemeContext.tsx","../src/theme/ThemeToggle.tsx","../src/components/PIDCanvas/PIDCanvas.tsx","../src/components/PIDCanvas/NodeRenderer.tsx","../src/diagram/symbols/mockSymbolLibrary.ts","../src/diagram/symbols/appearanceSchema.ts","../src/diagram/badges/nodeBadgeGlyphs.ts","../src/components/PIDCanvas/PipeRenderer.tsx","../src/components/PIDCanvas/DataOverlay.tsx","../src/components/PIDCanvas/PositionPanel.tsx","../src/diagram/hooks/useViewBox.ts","../src/diagram/hooks/useSelection.ts","../src/diagram/hooks/useTelemetry.ts","../src/diagram/hooks/useTelemetryStatus.ts","../src/diagram/hooks/useBooleanIndicators.ts","../src/diagram/hooks/useDrag.ts","../src/diagram/hooks/useKeyboardShortcuts.ts","../src/diagram/utils/portCalculations.ts","../src/components/SymbolLibrary/SymbolLibrary.tsx","../src/components/NodeConfigPanel/NodeConfigPanel.tsx","../src/diagram/utils/serialization.ts","../src/diagram/examples/exampleDiagram.ts","../src/utils/thresholds.ts","../src/diagram/schema/DeviceControlBinding.ts","../src/hooks/useDeviceControls.ts","../src/diagram/schema/DeviceConfigBinding.ts","../src/hooks/useDeviceConfigs.ts","../src/components/DeviceConfigPanel/DeviceConfigPanel.tsx","../src/diagram/hooks/useSimulation.ts"],"sourcesContent":["import React from 'react';\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: 'primary' | 'secondary';\n children: React.ReactNode;\n}\n\nexport const Button: React.FC<ButtonProps> = ({\n variant = 'primary',\n children,\n className = '',\n ...props\n}) => {\n // Use CSS classes for base and variant styles\n const baseClass = 'ui-btn';\n const variantClass = variant ? `ui-btn--${variant}` : '';\n return (\n <button className={`${baseClass} ${variantClass} ${className}`.trim()} {...props}>\n {children}\n </button>\n );\n};\n","import React, { useState } from 'react';\nimport { Selector, SelectorOption } from '../Selector';\nimport { LegacyValueEntry as ValueEntry } from '../ValueEntry';\nimport { Button } from '../Button';\nimport type { ScalingMetadata } from '../../diagram/schema/TelemetryBinding';\n\nexport interface ModeConfig {\n /** Units for this mode */\n unit?: string;\n /** Label for the primary setpoint */\n label?: string;\n /** Minimum value for primary setpoint */\n min?: number;\n /** Maximum value for primary setpoint */\n max?: number;\n /** Precision for primary setpoint */\n precision?: number;\n /** Whether this mode has a second setpoint (like ramp time) */\n hasSecondSetpoint?: boolean;\n /** Label for the second setpoint */\n secondLabel?: string;\n /** Units for the second setpoint */\n secondUnit?: string;\n /** Min value for second setpoint */\n secondMin?: number;\n /** Max value for second setpoint */\n secondMax?: number;\n /** Precision for second setpoint */\n secondPrecision?: number;\n}\n\nexport interface ControlPanelProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Panel title/identifier */\n title: string;\n /** Currently selected mode */\n selectedMode: string;\n /** Available mode options */\n modeOptions: SelectorOption[];\n /** Callback when mode changes */\n onModeChange: (mode: string) => void;\n /** Typed/target setpoint value (editable) */\n setpointValue: number | string;\n /** Callback when setpoint changes */\n onSetpointChange: (value: number | string) => void;\n /**\n * The setpoint the control loop is currently running: read-only feedback\n * from live telemetry, shown beside the typed setpoint above so an\n * operator sees target and current separately (the typed field never\n * \"snaps back\" to reflect the live value).\n *\n * Optional and hidden by default: the caller decides when a live-feedback\n * row is worth showing, since always showing a normally-differing current\n * value can read as a fault when none exists. When this tag carries a\n * maintenance force, the caller MUST pass this prop at that time - a\n * hidden value shows nothing, forced or not, but a shown value must carry\n * its FORCED mark (#3607, R19).\n */\n currentSetpointValue?: number | string;\n /**\n * True when `currentSetpointValue` is a maintenance force, not a measured\n * reading. Ignored when `currentSetpointValue` is not provided.\n */\n currentSetpointForced?: boolean;\n /**\n * The real, unforced value for `currentSetpointValue`. Optional; absent\n * until the backend publishes it (alphinity-tffi unit U3b).\n */\n currentSetpointRealValue?: number | string;\n /** Current second setpoint value (for ramp mode, etc.) */\n secondSetpointValue?: number | string;\n /** Callback when second setpoint changes */\n onSecondSetpointChange?: (value: number | string) => void;\n /** Configuration for each mode */\n modeConfigs?: Record<string, ModeConfig>;\n /** Default unit when no mode config is found */\n defaultUnit?: string;\n /** Default label when no mode config is found */\n defaultLabel?: string;\n /** Default min when no mode config is found */\n defaultMin?: number;\n /** Default max when no mode config is found */\n defaultMax?: number;\n /** Default precision when no mode config is found */\n defaultPrecision?: number;\n /**\n * Uniform scaling / EU metadata (#1663). When provided, supplies the\n * units / min / max / precision defaults from the live published scaling\n * record - used only where an explicit `default*` prop or a mode config does\n * not already set the value (modeConfig > explicit default > scaling >\n * hardcoded). Lets the consumer drive the panel from the scaling stream\n * instead of static diagram config.\n */\n scaling?: ScalingMetadata;\n /** Status state for the entire panel */\n status?: 'normal' | 'alarm' | 'warning' | 'disabled';\n /** Whether the panel is collapsed */\n collapsed?: boolean;\n /** Callback when collapse state changes */\n onCollapseChange?: (collapsed: boolean) => void;\n /** Panel size variant */\n size?: 'small' | 'medium' | 'large';\n /** Whether setpoint is read-only */\n setpointReadOnly?: boolean;\n /** Whether to show start/stop buttons */\n showStartStopButtons?: boolean;\n /** Whether the process is currently running */\n isRunning?: boolean;\n /** Callback when start button is clicked */\n onStart?: () => void;\n /** Callback when stop button is clicked */\n onStop?: () => void;\n /** Custom start button text */\n startButtonText?: string;\n /** Custom stop button text */\n stopButtonText?: string;\n}\n\nexport const ControlPanel: React.FC<ControlPanelProps> = ({\n title,\n selectedMode,\n modeOptions,\n onModeChange,\n setpointValue,\n onSetpointChange,\n currentSetpointValue,\n currentSetpointForced = false,\n currentSetpointRealValue,\n secondSetpointValue,\n onSecondSetpointChange,\n modeConfigs = {},\n defaultUnit,\n defaultLabel = 'Setpoint',\n defaultMin,\n defaultMax,\n defaultPrecision,\n scaling,\n status = 'normal',\n collapsed = false,\n onCollapseChange,\n size = 'medium',\n setpointReadOnly = false,\n showStartStopButtons = false,\n isRunning = false,\n onStart,\n onStop,\n startButtonText = 'Start',\n stopButtonText = 'Stop',\n className = '',\n ...props\n}) => {\n const [isExpanded, setIsExpanded] = useState(!collapsed);\n\n const toggleExpanded = () => {\n const newExpanded = !isExpanded;\n setIsExpanded(newExpanded);\n onCollapseChange?.(!newExpanded);\n };\n\n // Get current mode configuration\n const currentModeConfig = modeConfigs[selectedMode] || {};\n // Resolution order: mode config > explicit default prop > scaling metadata\n // (#1663) > hardcoded fallback.\n const currentUnit = currentModeConfig.unit || defaultUnit || scaling?.units || 'Units';\n const currentLabel = currentModeConfig.label || defaultLabel;\n const currentMin = currentModeConfig.min ?? defaultMin ?? scaling?.euLow ?? 0;\n const currentMax = currentModeConfig.max ?? defaultMax ?? scaling?.euHigh ?? 100;\n const currentPrecision =\n currentModeConfig.precision ?? defaultPrecision ?? scaling?.decimals ?? 2;\n const hasSecondSetpoint = currentModeConfig.hasSecondSetpoint || false;\n\n // Use CSS classes for base, status, size, and expanded state\n const baseClass = 'ui-control-panel';\n const statusClass = status ? `ui-control-panel--${status}` : '';\n const sizeClass = size ? `ui-control-panel--${size}` : '';\n const expandedClass = isExpanded ? 'is-expanded' : 'is-collapsed';\n const collapsibleClass = onCollapseChange ? 'is-collapsible' : '';\n\n return (\n <div\n className={`${baseClass} ${statusClass} ${sizeClass} ${expandedClass} ${collapsibleClass} ${className}`.trim()}\n {...props}\n >\n {/* Status Badge */}\n <div className={`ui-control-panel-badge ui-control-panel-badge--${status}`} />\n\n {/* Header */}\n <div\n className=\"ui-control-panel-header\"\n onClick={onCollapseChange ? toggleExpanded : undefined}\n >\n <h3 className=\"ui-control-panel-title\">{title}</h3>\n {onCollapseChange && (\n <svg\n className=\"ui-control-panel-chevron\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n )}\n </div>\n\n {/* Content */}\n <div className=\"ui-control-panel-content\">\n {/* Mode Selector */}\n <div>\n <Selector\n value={selectedMode}\n onChange={onModeChange}\n options={modeOptions}\n label=\"Control Mode\"\n status={status}\n size={size === 'small' ? 'small' : size === 'large' ? 'large' : 'medium'}\n disabled={status === 'disabled'}\n />\n </div>\n\n {/* Primary Setpoint Value Entry (typed/target) */}\n <div>\n <ValueEntry\n value={setpointValue}\n onChange={onSetpointChange}\n label={currentLabel}\n unit={currentUnit}\n min={currentMin}\n max={currentMax}\n precision={currentPrecision}\n status={status}\n readOnly={setpointReadOnly || status === 'disabled'}\n disabled={status === 'disabled'}\n />\n </div>\n\n {/* Current setpoint (read-only feedback). Hidden unless the caller\n passes a value - see currentSetpointValue doc comment. */}\n {currentSetpointValue !== undefined && (\n <div>\n <ValueEntry\n value={currentSetpointValue}\n onChange={() => {}}\n label={`${currentLabel} (Current)`}\n unit={currentUnit}\n precision={currentPrecision}\n status={status}\n readOnly\n forced={currentSetpointForced}\n realValue={currentSetpointRealValue}\n />\n </div>\n )}\n\n {/* Second Setpoint (for ramp mode, etc.) */}\n {hasSecondSetpoint && onSecondSetpointChange && (\n <div>\n <ValueEntry\n value={secondSetpointValue || 0}\n onChange={onSecondSetpointChange}\n label={currentModeConfig.secondLabel || 'Time'}\n unit={currentModeConfig.secondUnit || 'sec'}\n min={currentModeConfig.secondMin ?? 0}\n max={currentModeConfig.secondMax ?? 999}\n precision={currentModeConfig.secondPrecision ?? 0}\n status={status}\n readOnly={setpointReadOnly || status === 'disabled'}\n disabled={status === 'disabled'}\n />\n </div>\n )}\n\n {/* Start/Stop Buttons */}\n {showStartStopButtons && (onStart || onStop) && (\n <div className=\"ui-control-panel-buttons\">\n {onStart && (\n <Button\n variant=\"primary\"\n onClick={onStart}\n disabled={isRunning || status === 'disabled'}\n className={isRunning ? 'is-running' : ''}\n >\n {startButtonText}\n </Button>\n )}\n {onStop && (\n <Button\n variant=\"secondary\"\n onClick={onStop}\n disabled={!isRunning || status === 'disabled'}\n className={`ui-control-panel-stop-btn${!isRunning ? ' is-disabled' : ''}`}\n >\n {stopButtonText}\n </Button>\n )}\n </div>\n )}\n\n {/* Additional Info Row */}\n <div className=\"ui-control-panel-info\">\n <span>Mode: {selectedMode.toUpperCase()}</span>\n <div className=\"ui-control-panel-info-values\">\n <span>\n SP: {String(setpointValue)}\n {currentUnit}\n </span>\n {hasSecondSetpoint && secondSetpointValue && (\n <span>\n {currentModeConfig.secondLabel}: {String(secondSetpointValue)}\n {currentModeConfig.secondUnit}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n};\n","import React, { useState, useRef, useEffect } from 'react';\n\nexport interface SelectorOption {\n /** Unique value for the option */\n value: string;\n /** Display label for the option */\n label: string;\n /** Whether this option is disabled */\n disabled?: boolean;\n /** Optional description for tooltip or additional context */\n description?: string;\n}\n\nexport interface SelectorProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Currently selected value */\n value: string;\n /** Callback when selection changes */\n onChange: (value: string) => void;\n /** List of available options */\n options: SelectorOption[];\n /** Placeholder text when no option is selected */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Status state for visual feedback */\n status?: 'normal' | 'alarm' | 'warning' | 'disabled';\n /** Size variant */\n size?: 'small' | 'medium' | 'large';\n /** Label for the selector */\n label?: string;\n /** Whether the dropdown should open upward */\n dropdownUp?: boolean;\n}\n\nexport const Selector: React.FC<SelectorProps> = ({\n value,\n onChange,\n options,\n placeholder = 'Select option...',\n disabled = false,\n status = 'normal',\n size = 'medium',\n label,\n dropdownUp: _dropdownUp = false,\n className = '',\n ...props\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const [focusedIndex, setFocusedIndex] = useState(-1);\n const selectorRef = useRef<HTMLDivElement>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (selectorRef.current && !selectorRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n setFocusedIndex(-1);\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Handle keyboard navigation\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (disabled) return;\n switch (e.key) {\n case 'Enter':\n case ' ':\n e.preventDefault();\n if (!isOpen) {\n setIsOpen(true);\n setFocusedIndex(\n Math.max(\n 0,\n options.findIndex((opt) => opt.value === value)\n )\n );\n } else if (focusedIndex >= 0) {\n const selectedOption = options[focusedIndex];\n if (!selectedOption.disabled) {\n onChange(selectedOption.value);\n setIsOpen(false);\n setFocusedIndex(-1);\n }\n }\n break;\n case 'Escape':\n setIsOpen(false);\n setFocusedIndex(-1);\n break;\n case 'ArrowDown':\n e.preventDefault();\n if (!isOpen) {\n setIsOpen(true);\n setFocusedIndex(\n Math.max(\n 0,\n options.findIndex((opt) => opt.value === value)\n )\n );\n } else {\n setFocusedIndex((prev) => {\n const nextIndex = Math.min(prev + 1, options.length - 1);\n return options[nextIndex]?.disabled ? prev : nextIndex;\n });\n }\n break;\n case 'ArrowUp':\n e.preventDefault();\n if (isOpen) {\n setFocusedIndex((prev) => {\n const nextIndex = Math.max(prev - 1, 0);\n return options[nextIndex]?.disabled ? prev : nextIndex;\n });\n }\n break;\n }\n };\n\n // CSS class names for base, status, and size\n const baseClass = 'ui-selector';\n const statusClass = status ? `ui-selector--${status}` : '';\n const sizeClass = size ? `ui-selector--${size}` : '';\n\n // Get current selected option\n const selectedOption = options.find((opt) => opt.value === value);\n\n return (\n <div className={`ui-selector-container ${className}`.trim()}>\n {label && <label className=\"ui-selector-label\">{label}</label>}\n <div ref={selectorRef} style={{ position: 'relative' }}>\n <div\n className={`${baseClass} ${statusClass} ${sizeClass}`.trim()}\n onClick={() => !disabled && setIsOpen(!isOpen)}\n onKeyDown={handleKeyDown}\n tabIndex={disabled ? -1 : 0}\n role=\"combobox\"\n aria-expanded={isOpen}\n aria-haspopup=\"listbox\"\n {...props}\n >\n <span className=\"ui-selector-value\">\n {selectedOption ? selectedOption.label : placeholder}\n </span>\n {/* Arrow Icon */}\n <svg className=\"ui-selector-arrow\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n </div>\n\n {/* Dropdown */}\n {isOpen && (\n <div ref={dropdownRef} className=\"ui-selector-dropdown\" role=\"listbox\">\n {options.map((option, index) => (\n <div\n key={option.value}\n className={`ui-selector-option${option.disabled ? ' is-disabled' : ''}${option.value === value ? ' is-selected' : ''}`}\n onClick={() => {\n if (!option.disabled) {\n onChange(option.value);\n setIsOpen(false);\n setFocusedIndex(-1);\n }\n }}\n onMouseEnter={() => !option.disabled && setFocusedIndex(index)}\n role=\"option\"\n aria-selected={option.value === value}\n aria-disabled={option.disabled}\n title={option.description}\n >\n <div className=\"ui-selector-option-label\">\n <span>{option.label}</span>\n {option.value === value && (\n <svg className=\"ui-selector-check\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path\n fillRule=\"evenodd\"\n d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n clipRule=\"evenodd\"\n />\n </svg>\n )}\n </div>\n {option.description && (\n <div className=\"ui-selector-option-desc\">{option.description}</div>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n};\n","import React, { useState, useCallback } from 'react';\n\nexport interface ValueEntryProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'> {\n /** Current value */\n value: number | string;\n /** Callback when value changes */\n onChange: (value: number | string) => void;\n /** Unit of measurement to display */\n unit?: string;\n /** Status state for visual feedback */\n status?: 'normal' | 'alarm' | 'warning' | 'disabled';\n /** Minimum allowed value (for numeric inputs) */\n min?: number;\n /** Maximum allowed value (for numeric inputs) */\n max?: number;\n /** Number of decimal places (for numeric inputs) */\n precision?: number;\n /** Input type - numeric or text */\n inputType?: 'numeric' | 'text';\n /** Label for the input */\n label?: string;\n /** Whether the input is read-only */\n readOnly?: boolean;\n /**\n * True when `value` is a maintenance force, not a measured reading\n * (#3607, R19). Source: the control program's Sts_SimulationActive flag.\n * Renders a FORCED badge; never changes `status`, which stays an\n * independent fact about the same reading.\n */\n forced?: boolean;\n /**\n * The real, unforced value, shown beside `value` when `forced` is true.\n * Optional: the backend does not publish this for every tag kind yet\n * (alphinity-tffi unit U3b). When absent, only the FORCED badge renders.\n */\n realValue?: number | string;\n}\n\nexport const ValueEntry: React.FC<ValueEntryProps> = ({\n value,\n onChange,\n unit,\n status = 'normal',\n min,\n max,\n precision = 2,\n inputType = 'numeric',\n label,\n readOnly = false,\n forced = false,\n realValue,\n className = '',\n disabled,\n ...props\n}) => {\n const [displayValue, setDisplayValue] = useState(String(value));\n const [isEditing, setIsEditing] = useState(false);\n\n const handleFocus = useCallback(() => {\n setIsEditing(true);\n setDisplayValue(String(value));\n }, [value]);\n\n const handleBlur = useCallback(() => {\n setIsEditing(false);\n let newValue: number | string = displayValue;\n\n if (inputType === 'numeric') {\n const numValue = parseFloat(displayValue);\n if (!isNaN(numValue)) {\n // Apply min/max constraints\n let constrainedValue = numValue;\n if (min !== undefined) constrainedValue = Math.max(min, constrainedValue);\n if (max !== undefined) constrainedValue = Math.min(max, constrainedValue);\n\n // Apply precision\n constrainedValue = parseFloat(constrainedValue.toFixed(precision));\n newValue = constrainedValue;\n setDisplayValue(String(constrainedValue));\n } else {\n // Invalid number, revert to original value\n setDisplayValue(String(value));\n return;\n }\n }\n\n if (newValue !== value) {\n onChange(newValue);\n }\n }, [displayValue, inputType, min, max, precision, value, onChange]);\n\n const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n setDisplayValue(e.target.value);\n }, []);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.currentTarget.blur();\n }\n if (e.key === 'Escape') {\n setDisplayValue(String(value));\n e.currentTarget.blur();\n }\n },\n [value]\n );\n\n // Use CSS classes for base, status, and unit styles\n const baseClass = 'ui-value-entry';\n const unitClass = unit ? 'ui-value-entry--with-unit' : '';\n const currentStatus = disabled || readOnly ? 'disabled' : status;\n const statusVariantClass = currentStatus ? `ui-value-entry--${currentStatus}` : '';\n const forcedClass = forced ? 'ui-value-entry--forced' : '';\n const hasRealValue = forced && realValue !== undefined;\n\n const displayText = isEditing\n ? displayValue\n : inputType === 'numeric'\n ? Number(value).toFixed(precision)\n : String(value);\n\n return (\n <div className=\"ui-value-entry-container\">\n {label && (\n <label className=\"ui-value-entry-label\">\n {label}\n {forced && (\n <span className=\"ui-value-entry-forced-badge\" aria-label=\"Forced\">\n F\n </span>\n )}\n </label>\n )}\n <div className=\"ui-value-entry-wrapper\">\n <input\n type={inputType === 'numeric' ? 'text' : 'text'}\n value={displayText}\n onChange={handleChange}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n disabled={disabled}\n readOnly={readOnly}\n className={`${baseClass} ${statusVariantClass} ${forcedClass} ${unitClass} ${className}`.trim()}\n {...props}\n />\n {unit && <span className=\"ui-value-entry-unit\">{unit}</span>}\n {status === 'alarm' && <div className=\"ui-value-entry-badge ui-value-entry-badge--alarm\" />}\n {status === 'warning' && (\n <div className=\"ui-value-entry-badge ui-value-entry-badge--warning\" />\n )}\n {!label && forced && (\n <span className=\"ui-value-entry-forced-badge\" aria-label=\"Forced\">\n F\n </span>\n )}\n </div>\n {hasRealValue && (\n <div className=\"ui-value-entry-real-value\">\n REAL {String(realValue)}\n {unit ? ` ${unit}` : ''}\n </div>\n )}\n </div>\n );\n};\n","import React, { useState, useCallback } from 'react';\nimport { FORCED_MARK_COLOR } from '../../theme/statusColors';\n\nexport interface LegacyValueEntryProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'> {\n /** Current value */\n value: number | string;\n /** Callback when value changes */\n onChange: (value: number | string) => void;\n /** Unit of measurement to display */\n unit?: string;\n /** Status state for visual feedback */\n status?: 'normal' | 'alarm' | 'warning' | 'disabled';\n /** Minimum allowed value (for numeric inputs) */\n min?: number;\n /** Maximum allowed value (for numeric inputs) */\n max?: number;\n /** Number of decimal places (for numeric inputs) */\n precision?: number;\n /** Input type - numeric or text */\n inputType?: 'numeric' | 'text';\n /** Label for the input */\n label?: string;\n /** Whether the input is read-only */\n readOnly?: boolean;\n /**\n * True when `value` is a maintenance force, not a measured reading\n * (#3607, R19). Renders a FORCED badge; never changes `status`.\n */\n forced?: boolean;\n /**\n * The real, unforced value, shown beside `value` when `forced` is true.\n * Optional; absent until the backend publishes it (unit U3b).\n */\n realValue?: number | string;\n}\n\nexport const LegacyValueEntry: React.FC<LegacyValueEntryProps> = ({\n value,\n onChange,\n unit,\n status = 'normal',\n min,\n max,\n precision = 2,\n inputType = 'numeric',\n label,\n readOnly = false,\n forced = false,\n realValue,\n className = '',\n disabled,\n ...props\n}) => {\n const [displayValue, setDisplayValue] = useState(String(value));\n const [isEditing, setIsEditing] = useState(false);\n\n const handleFocus = useCallback(() => {\n setIsEditing(true);\n setDisplayValue(String(value));\n }, [value]);\n\n const handleBlur = useCallback(() => {\n setIsEditing(false);\n let newValue: number | string = displayValue;\n\n if (inputType === 'numeric') {\n const numValue = parseFloat(displayValue);\n if (!isNaN(numValue)) {\n // Apply min/max constraints\n let constrainedValue = numValue;\n if (min !== undefined) constrainedValue = Math.max(min, constrainedValue);\n if (max !== undefined) constrainedValue = Math.min(max, constrainedValue);\n\n // Apply precision\n constrainedValue = parseFloat(constrainedValue.toFixed(precision));\n newValue = constrainedValue;\n setDisplayValue(String(constrainedValue));\n } else {\n // Invalid number, revert to original value\n setDisplayValue(String(value));\n return;\n }\n }\n\n if (newValue !== value) {\n onChange(newValue);\n }\n }, [displayValue, inputType, min, max, precision, value, onChange]);\n\n const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n setDisplayValue(e.target.value);\n }, []);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.currentTarget.blur();\n }\n if (e.key === 'Escape') {\n setDisplayValue(String(value));\n e.currentTarget.blur();\n }\n },\n [value]\n );\n\n // Create the display text\n const displayText = isEditing\n ? displayValue\n : inputType === 'numeric'\n ? Number(value).toFixed(precision)\n : String(value);\n\n // Status-based styling with modern industrial colors\n const getStatusStyles = () => {\n switch (status) {\n case 'alarm':\n return {\n borderColor: '#ef4444',\n backgroundColor: 'rgba(239, 68, 68, 0.15)',\n color: '#fecaca',\n boxShadow: '0 0 20px rgba(239, 68, 68, 0.3)',\n };\n case 'warning':\n return {\n borderColor: '#f59e0b',\n backgroundColor: 'rgba(245, 158, 11, 0.15)',\n color: '#fed7aa',\n boxShadow: '0 0 20px rgba(245, 158, 11, 0.3)',\n };\n case 'disabled':\n return {\n borderColor: '#6b7280',\n backgroundColor: 'rgba(107, 114, 128, 0.1)',\n color: '#9ca3af',\n cursor: 'not-allowed',\n };\n default:\n return {\n borderColor: '#64748b',\n backgroundColor: 'rgba(30, 41, 59, 0.9)',\n color: '#f1f5f9',\n boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',\n };\n }\n };\n\n const statusStyles = getStatusStyles();\n\n const forcedBadgeStyle: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '1.1rem',\n height: '1.1rem',\n fontSize: '0.7rem',\n fontWeight: 700,\n color: FORCED_MARK_COLOR.text,\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n borderRadius: '50%',\n marginLeft: '8px',\n lineHeight: 1,\n };\n\n const realValueStyle: React.CSSProperties = {\n fontSize: '0.75rem',\n fontWeight: 500,\n color: '#94a3b8',\n letterSpacing: '0.02em',\n };\n\n const inputStyle: React.CSSProperties = {\n width: '12rem',\n height: '4rem',\n padding: unit ? '1rem 4rem 1rem 1.5rem' : '1rem 1.5rem',\n border: '2px solid',\n borderRadius: '1.5rem',\n fontFamily:\n 'ui-monospace, SFMono-Regular, \"SF Mono\", Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace',\n fontSize: '1.25rem',\n fontWeight: 'bold',\n textAlign: 'center',\n outline: 'none',\n transition: 'all 300ms ease',\n backdropFilter: 'blur(8px)',\n ...statusStyles,\n };\n\n const inputStyleFocus: React.CSSProperties = {\n ...inputStyle,\n borderColor: '#60a5fa',\n boxShadow:\n '0 0 0 4px rgba(96, 165, 250, 0.3), ' +\n (statusStyles.boxShadow || '0 8px 32px rgba(0, 0, 0, 0.3)'),\n transform: 'scale(1.02)',\n };\n\n const unitStyle: React.CSSProperties = {\n position: 'absolute',\n right: '0.75rem',\n top: '50%',\n transform: 'translateY(-50%)',\n padding: '0.5rem 0.75rem',\n fontSize: '1rem',\n fontWeight: 'bold',\n color: '#ffffff',\n backgroundColor: '#3b82f6',\n borderRadius: '0.5rem',\n border: '2px solid #60a5fa',\n backdropFilter: 'blur(4px)',\n pointerEvents: 'none' as const,\n zIndex: 10,\n boxShadow: '0 2px 8px rgba(59, 130, 246, 0.4)',\n };\n\n return (\n <div\n style={{\n display: 'inline-flex',\n flexDirection: 'column',\n alignItems: 'center',\n gap: '0.75rem',\n }}\n >\n {(label || forced) && (\n <label\n style={{\n display: 'flex',\n alignItems: 'center',\n fontSize: '0.875rem',\n fontWeight: '600',\n color: '#cbd5e1',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }}\n >\n {label}\n {forced && (\n <span style={forcedBadgeStyle} aria-label=\"Forced\">\n F\n </span>\n )}\n </label>\n )}\n <div style={{ position: 'relative', display: 'inline-flex', alignItems: 'center' }}>\n <input\n type=\"text\"\n value={displayText}\n onChange={handleChange}\n onFocus={(e) => {\n Object.assign(e.target.style, inputStyleFocus);\n handleFocus();\n }}\n onBlur={(e) => {\n Object.assign(e.target.style, inputStyle);\n handleBlur();\n }}\n onKeyDown={handleKeyDown}\n disabled={disabled}\n readOnly={readOnly}\n style={inputStyle}\n {...props}\n />\n {unit && <span style={unitStyle}>{unit}</span>}\n {status === 'alarm' && (\n <div\n style={{\n position: 'absolute',\n top: '-8px',\n right: '-8px',\n width: '1rem',\n height: '1rem',\n backgroundColor: '#ef4444',\n borderRadius: '50%',\n boxShadow: '0 0 10px rgba(239, 68, 68, 0.8)',\n animation: 'pulse 2s infinite',\n }}\n />\n )}\n {status === 'warning' && (\n <div\n style={{\n position: 'absolute',\n top: '-8px',\n right: '-8px',\n width: '1rem',\n height: '1rem',\n backgroundColor: '#f59e0b',\n borderRadius: '50%',\n boxShadow: '0 0 10px rgba(245, 158, 11, 0.8)',\n }}\n />\n )}\n </div>\n {forced && realValue !== undefined && (\n <div style={realValueStyle}>\n REAL {String(realValue)}\n {unit ? ` ${unit}` : ''}\n </div>\n )}\n </div>\n );\n};\n","/**\n * ISA-101 High Performance HMI — Unified Status Color System\n *\n * Single source of truth for mapping device status to visual colors.\n * Used by NodeRenderer (SVG fill/stroke), DeviceControlPanel (status indicator),\n * and DataOverlay (telemetry badge).\n */\n\n/**\n * Canonical device status vocabulary.\n *\n * `unknown` marks a value the backend does not trust. OPC UA publishes this\n * condition as StatusCode severity Bad. The library never derives `unknown`\n * from a number or a threshold. The library only displays an `unknown` value\n * that the backend has already published.\n */\nexport type DeviceStatus =\n | 'normal'\n | 'warning'\n | 'alarm'\n | 'fault'\n | 'off'\n | 'starting'\n | 'stopping'\n | 'unknown'\n | 'forced';\n\n/** Color entry covering both SVG node and overlay/panel needs */\nexport interface StatusColorEntry {\n /** Fill color for SVG node symbols */\n fill: string;\n /** Stroke color for SVG node symbols */\n stroke: string;\n /** Background color for overlays and panel indicators */\n bg: string;\n /** Text color for overlays */\n text: string;\n /** Border color for overlays */\n border: string;\n}\n\n/** Partial map for consumer overrides — only specify statuses you want to change */\nexport type StatusColorMap = Partial<Record<DeviceStatus, Partial<StatusColorEntry>>>;\n\n/**\n * The ISA unknown token, shared with the consuming application.\n *\n * The same token already marks an untrusted value on the consumer's Sidebar\n * and Interlocks screens. The library reuses it so one meaning has one\n * appearance across both products. See views-library issue #27.\n *\n * The library uses this one token for both unknown triggers: a node with\n * `status: 'unknown'` (an untrusted device) and a node with\n * `positionUnknown: true` (a disclaimed position). Both mean that the system\n * cannot confirm the state, so both get the same appearance.\n *\n * The hatch carries the meaning, not the fill. The flat `bg` sits a CIEDE2000\n * delta-E of 0.45 from the `off` fill after greyscale conversion, so the fill\n * alone cannot separate an untrusted device from a confirmed-off one. Any\n * surface that uses this token must also carry the hatch or a glyph.\n *\n * The hue is deliberate. Periwinkle at hue 240 sits between override violet\n * and interaction blue, so an operator cannot confuse the three.\n */\nexport const ISA_UNKNOWN = {\n fg: '#4f4f8a',\n bg: '#ebebf5',\n border: '#8f8fc2',\n} as const;\n\n/**\n * The dark-theme counterpart of `ISA_UNKNOWN`.\n *\n * `DEFAULT_STATUS_COLORS` carries the light values. Wiring a theme switch is\n * not part of this change; the constant exists so a consumer and a later\n * change can use the agreed values rather than inventing new ones.\n */\nexport const ISA_UNKNOWN_DARK = {\n fg: '#b9bbdb',\n bg: '#23263a',\n border: '#6f74a3',\n} as const;\n\n/**\n * Maintenance-force marking color (#3607, R19). A forced value is not a\n * health status, and must never share a color with warning/alarm/fault or\n * read as one of them. Violet is unused elsewhere in this palette.\n *\n * `DEFAULT_STATUS_COLORS.forced` points at this same object (alphinity-tffi\n * #3771). A device that a maintenance force holds then gets the forced\n * violet on its status dot, and not the `off` grey fallback. The forced\n * entry is a marker, not a severity: it stays outside the\n * warning/alarm/fault colours.\n *\n * Light fill + dark text, not a solid vivid chip: reads as a calm marker\n * rather than an alarm sticker, while the vivid `border` shade still\n * outlines a forced value's box/ring so it stays findable at a glance.\n */\nexport const FORCED_MARK_COLOR: StatusColorEntry = {\n fill: '#ede9fe',\n stroke: '#5b21b6',\n bg: '#ede9fe',\n text: '#5b21b6',\n border: '#7c3aed',\n};\n\n/**\n * ISA-101 defaults.\n *\n * - normal: muted grey (ISA-101: no color for normal operation)\n * - warning: amber/yellow\n * - alarm: red\n * - fault: dark red\n * - off: light grey (matches canvas default #edeeef)\n * - starting: light green (transitional)\n * - stopping: amber (transitional)\n * - unknown: periwinkle (untrusted read; OPC UA severity Bad). Derived\n * from ISA_UNKNOWN above, the shared token.\n * - forced: violet (a maintenance force holds the device). The same\n * object as FORCED_MARK_COLOR above.\n *\n * The unknown entry deliberately does NOT rely on its fill. The flat fill\n * sits a CIEDE2000 delta-E of 0.45 from the off fill after greyscale\n * conversion, so the two are indistinguishable without colour.\n *\n * Every unknown surface therefore carries a second channel that does not\n * depend on colour. Fixed-layout surfaces add a glyph. The P&ID symbol adds\n * a hatch. A surface that shows this fill alone reproduces the defect the\n * unknown status exists to close.\n *\n * This palette is not fully greyscale-separable: normal and alarm sit 1.6\n * apart. That is why unknown never relies on colour anywhere.\n */\nexport const DEFAULT_STATUS_COLORS: Record<DeviceStatus, StatusColorEntry> = {\n normal: { fill: '#888888', stroke: '#6b6b6b', bg: '#888888', text: '#ffffff', border: '#6b6b6b' },\n warning: { fill: '#f59e0b', stroke: '#d97706', bg: '#FFD700', text: '#000000', border: '#d97706' },\n alarm: { fill: '#ef4444', stroke: '#dc2626', bg: '#FF0000', text: '#ffffff', border: '#dc2626' },\n fault: { fill: '#dc2626', stroke: '#b91c1c', bg: '#FF0000', text: '#ffffff', border: '#b91c1c' },\n off: { fill: '#edeeef', stroke: '#000000', bg: '#666666', text: '#ffffff', border: '#4b5563' },\n starting: { fill: '#90EE90', stroke: '#22c55e', bg: '#90EE90', text: '#000000', border: '#22c55e' },\n stopping: { fill: '#FFD700', stroke: '#d97706', bg: '#FFD700', text: '#000000', border: '#d97706' },\n unknown: { fill: ISA_UNKNOWN.bg, stroke: ISA_UNKNOWN.border, bg: ISA_UNKNOWN.bg, text: ISA_UNKNOWN.fg, border: ISA_UNKNOWN.border },\n forced: FORCED_MARK_COLOR,\n};\n\n\n/** Backward-compatible aliases for old status vocabulary */\nconst STATUS_ALIASES: Record<string, DeviceStatus> = {\n running: 'normal',\n stopped: 'off',\n};\n\n/**\n * Normalize a status string to its canonical `DeviceStatus` key.\n *\n * The function lowercases the value and follows `STATUS_ALIASES`. Every\n * caller that must decide \"is this status X?\" uses this function. A caller\n * that compares a raw string instead can disagree with `resolveStatusColor`\n * once an alias changes, which would let one surface paint a status the\n * other surface does not recognise.\n */\nexport function normalizeDeviceStatus(status: string | undefined): DeviceStatus {\n const raw = (status || 'off').toLowerCase();\n return (STATUS_ALIASES[raw] ?? raw) as DeviceStatus;\n}\n\n/**\n * Resolve a status string to a full color entry.\n *\n * Priority: customMap[status] (merged) > DEFAULT_STATUS_COLORS[status] > off fallback.\n * Supports aliases ('running' -> 'normal', 'stopped' -> 'off').\n */\nexport function resolveStatusColor(\n status: string | undefined,\n customMap?: StatusColorMap,\n): StatusColorEntry {\n const key = normalizeDeviceStatus(status);\n const defaults = DEFAULT_STATUS_COLORS[key] ?? DEFAULT_STATUS_COLORS.off;\n const overrides = customMap?.[key];\n if (!overrides) return defaults;\n return { ...defaults, ...overrides };\n}\n","import React from 'react';\nimport {\n UnknownStatusGlyph,\n UNKNOWN_GLYPH_FILL_STYLE,\n unknownGlyphHostStyle,\n} from '../../theme/unknownStatusGlyph';\n\nconst getStatusDescription = (status: string): string => {\n switch (status) {\n case 'alarm':\n return 'Critical alarm condition requiring attention';\n case 'warning':\n return 'Warning condition that should be monitored';\n case 'fault':\n return 'Equipment fault detected';\n case 'active':\n return 'Equipment is actively running';\n case 'normal':\n return 'Operating normally';\n case 'off':\n return 'Equipment is off';\n case 'unknown':\n return 'Device state cannot be confirmed. Check the link or the wiring.';\n default:\n return '';\n }\n};\n\n// Hide the description node with an inline style, not a CSS class. The\n// package ships no stylesheet, so a class-based `sr-only` rule would never\n// reach a consumer. This is the standard visually-hidden clip technique: it\n// keeps the text readable by assistive tech without affecting layout.\nconst visuallyHiddenStyle: React.CSSProperties = {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: 0,\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: 0,\n};\n\nexport interface StatusIndicatorProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Status state determining color and behavior.\n *\n * `unknown` marks a value the backend does not trust. Set `unknown` only\n * from a published bad-quality read. Do not derive `unknown` from a number\n * or a threshold in this component.\n */\n status: 'off' | 'normal' | 'alarm' | 'warning' | 'active' | 'fault' | 'unknown';\n /** Size of the indicator */\n size?: 'small' | 'medium' | 'large';\n /**\n * Animation behavior. Default is `none`.\n *\n * ISA-18.2 reserves blinking for an unacknowledged alarm. The `unknown`\n * status does not clamp this prop to `none`. Pass `animation` only when\n * the alarm state of the point needs it.\n */\n animation?: 'none' | 'pulse' | 'blink' | 'flash';\n /** Label text to display next to indicator */\n label?: string;\n /** Position of label relative to indicator */\n labelPosition?: 'left' | 'right' | 'top' | 'bottom';\n /** Shape of the indicator */\n shape?: 'circle' | 'square' | 'diamond';\n /** Whether the indicator should have a 3D LED effect */\n led?: boolean;\n /** Whether the indicator is rendered at all. When false, nothing is rendered. Defaults to true. */\n visible?: boolean;\n /**\n * True when the underlying tag's state is a maintenance force, not a\n * measured reading (#3607, R19). Renders a small glyph badge, never a\n * color change: this indicator's whole signal is color plus a short\n * label today, and color alone fails a colorblind operator.\n */\n forced?: boolean;\n /**\n * The real, unforced state, shown as text beside `label` when `forced`\n * is true. For a forced discrete OUTPUT (a lamp, a valve OPEN/CLOSED\n * indicator - R2), this is the state the system would command if the\n * force were not active, not a second physical reading. Optional:\n * absent until the backend publishes it. When absent, only the forced\n * glyph badge renders.\n */\n realValue?: string;\n}\n\nexport const StatusIndicator: React.FC<StatusIndicatorProps> = ({\n status,\n size = 'medium',\n animation = 'none',\n label,\n labelPosition = 'right',\n shape = 'circle',\n led = true,\n visible = true,\n forced = false,\n realValue,\n className = '',\n style,\n 'aria-describedby': consumerDescribedBy,\n children,\n ...props\n}) => {\n // A stable id per instance, so two indicators on one page never collide.\n // Call the hook before the `visible` early return below. A hook after a\n // conditional return breaks the Rules of Hooks when `visible` toggles.\n const generatedDescriptionId = `${React.useId()}-status-description`;\n\n if (!visible) {\n return null;\n }\n\n // Compose, do not choose: `aria-describedby` accepts a space-separated\n // list of ids. Append the library's id to a consumer-supplied one so\n // neither description is lost.\n const describedBy = consumerDescribedBy\n ? `${consumerDescribedBy} ${generatedDescriptionId}`\n : generatedDescriptionId;\n\n const description = getStatusDescription(status);\n\n // Use CSS classes for base, status, size, shape, animation, led\n const baseClass = 'ui-status-indicator';\n const statusClass = status ? `ui-status-indicator--${status}` : '';\n const sizeClass = size ? `ui-status-indicator--${size}` : '';\n const shapeClass = shape ? `ui-status-indicator--${shape}` : '';\n const animationClass = animation ? `ui-status-indicator--${animation}` : '';\n const ledClass = led ? 'ui-status-indicator--led' : '';\n\n const isUnknown = status === 'unknown';\n\n // The host element has no `position` set by the library elsewhere. The\n // shared helper sets `relative` only for the `unknown` glyph, so the glyph\n // can fill the element with an absolute inset. No consumer CSS is needed.\n const indicatorStyle = unknownGlyphHostStyle(isUnknown, style);\n\n // Container layout based on label position\n const getContainerLayout = () => {\n switch (labelPosition) {\n case 'left':\n return 'ui-status-indicator-container ui-status-indicator-label-left';\n case 'right':\n return 'ui-status-indicator-container ui-status-indicator-label-right';\n case 'top':\n return 'ui-status-indicator-container ui-status-indicator-label-top';\n case 'bottom':\n return 'ui-status-indicator-container ui-status-indicator-label-bottom';\n default:\n return 'ui-status-indicator-container ui-status-indicator-label-right';\n }\n };\n\n const forcedClass = forced ? 'ui-status-indicator--forced' : '';\n\n const hasRealValue = forced && realValue !== undefined;\n\n return (\n <span className={getContainerLayout()}>\n {label && (labelPosition === 'left' || labelPosition === 'top') && (\n <span className=\"ui-status-indicator-label\">{label}</span>\n )}\n {hasRealValue && (labelPosition === 'left' || labelPosition === 'top') && (\n <span className=\"ui-status-indicator-real-value\">REAL {realValue}</span>\n )}\n <span style={{ position: 'relative', display: 'inline-flex' }}>\n <span\n role=\"status\"\n aria-label={label || `Status: ${status}`}\n aria-description={description}\n aria-describedby={describedBy}\n data-status={status}\n data-forced={forced}\n className={`\n ${baseClass}\n ${statusClass}\n ${sizeClass}\n ${shapeClass}\n ${animationClass}\n ${ledClass}\n ${forcedClass}\n ${className}\n `\n .replace(/\\s+/g, ' ')\n .trim()}\n style={indicatorStyle}\n {...props}\n >\n {isUnknown && (\n <UnknownStatusGlyph\n size={size}\n counterRotate={shape === 'diamond'}\n style={UNKNOWN_GLYPH_FILL_STYLE}\n />\n )}\n {/* Keep rendering any children the consumer passed. The element used\n to be self-closing, so children arrived through the props spread;\n explicit children would otherwise drop them silently. */}\n {children}\n <span id={generatedDescriptionId} style={visuallyHiddenStyle}>\n {description}\n </span>\n </span>\n {/* The forced badge (#26) overhangs the top-right corner, outside the\n indicator body that the unknown glyph fills. A value can be both\n forced and untrusted, so the two markers must stay separately\n readable. */}\n {forced && (\n <span className=\"ui-status-indicator-forced-badge\" aria-label=\"Forced\">\n F\n </span>\n )}\n </span>\n {label && (labelPosition === 'right' || labelPosition === 'bottom') && (\n <span className=\"ui-status-indicator-label\">{label}</span>\n )}\n {hasRealValue && (labelPosition === 'right' || labelPosition === 'bottom') && (\n <span className=\"ui-status-indicator-real-value\">REAL {realValue}</span>\n )}\n </span>\n );\n};\n","import React from 'react';\n\n/**\n * Shared glyph for the `unknown` device status (KTD10).\n *\n * `StatusIndicator`, `DataOverlay`, and `DeviceControlPanel` import this one\n * module. One drawing keeps the marker consistent. An operator learns the\n * marker once and recognises it on every surface.\n *\n * The glyph is a question mark inside a dashed ring. The dashed ring is a\n * second channel that does not depend on colour. A broken outline reads as\n * an incomplete circuit.\n */\n\n/** Size keys the glyph accepts. These match `StatusIndicator`'s `size` prop. */\nexport type UnknownGlyphSize = 'small' | 'medium' | 'large';\n\n/**\n * CSS custom property that colours the glyph.\n *\n * A consumer sets this property on any ancestor to retheme the glyph.\n * `StatusIndicator` scopes it under the `ui-status-indicator--unknown` class.\n * The overlay and the control panel carry no such class, so a consumer sets\n * the property directly on those hosts. The glyph falls back to\n * `currentColor` when the consumer sets nothing, so the package never has to\n * ship CSS for it.\n */\nexport const UNKNOWN_STATUS_GLYPH_COLOR_VAR = '--ui-status-indicator-unknown-glyph-color';\n\ninterface UnknownGlyphStrokeWeights {\n /** Stroke width of the dashed ring. */\n ring: number;\n /** Dash pattern of the ring, as an SVG `stroke-dasharray` value. */\n ringDash: string;\n /** Stroke width of the question-mark hook. */\n mark: number;\n /** Radius of the question-mark dot. */\n dot: number;\n}\n\n/**\n * Per-size stroke weights.\n *\n * Small sizes use heavier strokes. A heavier stroke keeps the 3:1 contrast\n * floor when the whole indicator is only 1rem wide.\n */\nconst UNKNOWN_GLYPH_STROKE_WEIGHTS: Record<UnknownGlyphSize, UnknownGlyphStrokeWeights> = {\n small: { ring: 4.5, ringDash: '7 6', mark: 5, dot: 2.9 },\n medium: { ring: 4, ringDash: '6.5 5.5', mark: 4.5, dot: 2.7 },\n large: { ring: 3.5, ringDash: '6.5 5.5', mark: 4, dot: 2.5 },\n};\n\n/**\n * Dash rhythm of the glyph's ring.\n *\n * The P&ID node symbol reuses this value for its own dashed outline. The\n * canvas cannot host a centred glyph, so it carries the same rhythm on the\n * symbol outline instead. Sharing the value keeps the two treatments in step.\n */\nexport const UNKNOWN_GLYPH_RING_DASH = UNKNOWN_GLYPH_STROKE_WEIGHTS.medium.ringDash;\n\n/**\n * Style that makes the glyph fill its host box.\n *\n * Use it with `unknownGlyphHostStyle` on an HTML host. An SVG host places the\n * glyph with `x`, `y`, `width` and `height` instead and must not use this\n * style. CSS `width` and `height` override the `width` and `height`\n * presentation attributes on an `svg` element, so a forced size here would\n * silently defeat an SVG host's own sizing.\n */\nexport const UNKNOWN_GLYPH_FILL_STYLE: React.CSSProperties = {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n};\n\n/**\n * Position an HTML host so the glyph can fill it.\n *\n * The host needs `position: relative` only while it shows the glyph. Any\n * caller-supplied style wins, so a consumer can still override the position.\n */\nexport function unknownGlyphHostStyle(\n isUnknown: boolean,\n style?: React.CSSProperties,\n): React.CSSProperties | undefined {\n return isUnknown ? { position: 'relative', ...style } : style;\n}\n\nexport interface UnknownStatusGlyphProps\n extends Omit<React.SVGProps<SVGSVGElement>, 'children' | 'viewBox'> {\n /** Size of the glyph. Sets the stroke weight (see `UNKNOWN_GLYPH_STROKE_WEIGHTS`). */\n size?: UnknownGlyphSize;\n /**\n * Counter-rotate the glyph.\n *\n * Set this to `true` when the host element applies `transform: rotate(45deg)`,\n * such as the `diamond` shape in `StatusIndicator`. The glyph then reads\n * upright instead of inheriting the host rotation.\n *\n * The counter-rotation is a fixed -45 degrees. It assumes the 45-degree\n * rotation that the documented `ui-status-indicator--diamond` rule applies.\n * A consumer that rotates the host by another angle must not set this flag.\n */\n counterRotate?: boolean;\n}\n\n/**\n * Renders the `unknown` status glyph as an inline `svg`.\n *\n * The glyph carries no CSS class dependency. It needs no stylesheet, so it\n * survives server-side rendering and needs no CSP nonce (KTD1).\n *\n * The `svg` is `aria-hidden` and `focusable=\"false\"`. A separate unit adds\n * the accessible description.\n *\n * Remaining `svg` props pass straight through. An HTML host sizes the glyph\n * with `style`; an SVG host places it with `x`, `y`, `width`, and `height`,\n * because a nested `svg` accepts those as user-unit attributes.\n */\nexport function UnknownStatusGlyph({\n size = 'medium',\n counterRotate = false,\n style,\n ...rest\n}: UnknownStatusGlyphProps): React.ReactElement {\n const weights = UNKNOWN_GLYPH_STROKE_WEIGHTS[size];\n\n return (\n <svg\n viewBox=\"0 0 48 48\"\n aria-hidden=\"true\"\n focusable=\"false\"\n style={{\n display: 'block',\n color: `var(${UNKNOWN_STATUS_GLYPH_COLOR_VAR}, currentColor)`,\n transform: counterRotate ? 'rotate(-45deg)' : undefined,\n ...style,\n }}\n {...rest}\n >\n <circle\n cx={24}\n cy={24}\n r={20}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeDasharray={weights.ringDash}\n strokeLinecap=\"round\"\n strokeWidth={weights.ring}\n />\n <path\n d=\"M18.4 18.6a5.8 5.8 0 1 1 6.4 8.1v3.1\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={weights.mark}\n />\n <circle cx={24.6} cy={35.4} r={weights.dot} fill=\"currentColor\" />\n </svg>\n );\n}\n","import React from 'react';\nimport { ControlPanel, type ModeConfig } from '../ControlPanel';\nimport type { SelectorOption } from '../Selector';\n\nexport interface NodeControlConfig {\n /** Node ID */\n nodeId: string;\n /** Display title for the control panel */\n title: string;\n /** Available mode options */\n modes: SelectorOption[];\n /** Current selected mode */\n currentMode: string;\n /** Callback when mode changes */\n onModeChange: (mode: string) => void;\n /** Typed/target setpoint value (editable) */\n setpoint: number | string;\n /** Callback when setpoint changes */\n onSetpointChange: (value: number | string) => void;\n /** Current setpoint: read-only feedback from live telemetry. See ControlPanelProps.currentSetpointValue. */\n currentSetpoint?: number | string;\n /** True when `currentSetpoint` is a maintenance force, not a measured reading. */\n currentSetpointForced?: boolean;\n /** The real, unforced value for `currentSetpoint`. Optional; absent until the backend publishes it. */\n currentSetpointRealValue?: number | string;\n /** Optional second setpoint value */\n secondSetpoint?: number | string;\n /** Callback for second setpoint */\n onSecondSetpointChange?: (value: number | string) => void;\n /** Configuration for each mode */\n modeConfigs?: Record<string, ModeConfig>;\n /** Whether device is currently running */\n isRunning?: boolean;\n /** Start command handler */\n onStart?: () => void;\n /** Stop command handler */\n onStop?: () => void;\n /** Custom start button text */\n startButtonText?: string;\n /** Custom stop button text */\n stopButtonText?: string;\n /** Current status */\n status?: 'normal' | 'alarm' | 'warning' | 'disabled';\n /** Whether setpoint is read-only */\n readOnly?: boolean;\n}\n\nexport interface NodeControlsPanelProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Control configuration for the selected node */\n config: NodeControlConfig | null;\n /** Callback to close the panel */\n onClose: () => void;\n /** Panel position - bottom (mobile) or side (desktop/large touch screens) */\n position?: 'bottom' | 'side';\n /** Whether to show in touch-optimized mode (larger targets) */\n touchOptimized?: boolean;\n}\n\n/**\n * NodeControlsPanel - Touch-friendly slide-out panel for device manual controls\n *\n * Optimized for industrial touch screens with:\n * - Large touch targets (minimum 44px)\n * - Single-tap interactions\n * - Clear visual feedback\n * - Bottom or side slide-in animation\n */\nexport const NodeControlsPanel: React.FC<NodeControlsPanelProps> = ({\n config,\n onClose,\n position = 'bottom',\n touchOptimized = true,\n className = '',\n style,\n ...props\n}) => {\n if (!config) return null;\n\n const isBottom = position === 'bottom';\n const touchClass = touchOptimized ? 'touch-optimized' : '';\n\n // Container styles\n const containerStyle: React.CSSProperties = {\n position: 'fixed',\n zIndex: 9999,\n background: 'white',\n boxShadow: '0 -4px 16px rgba(0, 0, 0, 0.15)',\n borderRadius: isBottom ? '16px 16px 0 0' : '0',\n animation: isBottom ? 'slideUp 0.3s ease-out' : 'slideInRight 0.3s ease-out',\n display: 'flex',\n flexDirection: 'column',\n ...(isBottom\n ? {\n left: 0,\n right: 0,\n bottom: 0,\n maxHeight: '70vh',\n borderTop: '1px solid #e5e7eb',\n }\n : {\n right: 0,\n top: 0,\n bottom: 0,\n width: touchOptimized ? '400px' : '320px',\n borderLeft: '1px solid #e5e7eb',\n }),\n ...style,\n };\n\n // Header styles\n const headerStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: touchOptimized ? '1.25rem 1.5rem' : '1rem',\n borderBottom: '2px solid #e5e7eb',\n background: '#f9fafb',\n flexShrink: 0,\n };\n\n const titleStyle: React.CSSProperties = {\n margin: 0,\n fontSize: touchOptimized ? '1.5rem' : '1.25rem',\n fontWeight: 600,\n color: '#111827',\n };\n\n // Close button styles (large touch target)\n const closeButtonStyle: React.CSSProperties = {\n background: '#ef4444',\n color: 'white',\n border: 'none',\n borderRadius: '8px',\n width: touchOptimized ? '56px' : '44px',\n height: touchOptimized ? '56px' : '44px',\n fontSize: touchOptimized ? '1.75rem' : '1.5rem',\n fontWeight: 'bold',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'all 0.2s ease',\n flexShrink: 0,\n };\n\n const contentStyle: React.CSSProperties = {\n padding: touchOptimized ? '1.5rem' : '1rem',\n overflowY: 'auto',\n flex: 1,\n };\n\n return (\n <>\n {/* Backdrop */}\n <div\n style={{\n position: 'fixed',\n inset: 0,\n background: 'rgba(0, 0, 0, 0.3)',\n zIndex: 9998,\n animation: 'fadeIn 0.3s ease-out',\n }}\n onClick={onClose}\n />\n\n {/* Panel */}\n <div\n className={`node-controls-panel ${touchClass} ${className}`.trim()}\n style={containerStyle}\n {...props}\n >\n {/* Header */}\n <div style={headerStyle}>\n <h2 style={titleStyle}>{config.title}</h2>\n <button\n style={closeButtonStyle}\n onClick={onClose}\n onMouseEnter={(e) => {\n e.currentTarget.style.background = '#dc2626';\n e.currentTarget.style.transform = 'scale(1.05)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = '#ef4444';\n e.currentTarget.style.transform = 'scale(1)';\n }}\n aria-label=\"Close\"\n >\n ✕\n </button>\n </div>\n\n {/* Content */}\n <div style={contentStyle}>\n <ControlPanel\n title={config.nodeId}\n selectedMode={config.currentMode}\n modeOptions={config.modes}\n onModeChange={config.onModeChange}\n setpointValue={config.setpoint}\n onSetpointChange={config.onSetpointChange}\n currentSetpointValue={config.currentSetpoint}\n currentSetpointForced={config.currentSetpointForced}\n currentSetpointRealValue={config.currentSetpointRealValue}\n secondSetpointValue={config.secondSetpoint}\n onSecondSetpointChange={config.onSecondSetpointChange}\n modeConfigs={config.modeConfigs}\n status={config.status}\n setpointReadOnly={config.readOnly}\n showStartStopButtons={!!(config.onStart || config.onStop)}\n isRunning={config.isRunning}\n onStart={config.onStart}\n onStop={config.onStop}\n startButtonText={config.startButtonText}\n stopButtonText={config.stopButtonText}\n size={touchOptimized ? 'large' : 'medium'}\n />\n </div>\n </div>\n\n {/* Animations */}\n <style>{`\n @keyframes slideUp {\n from {\n transform: translateY(100%);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n }\n\n @keyframes slideInRight {\n from {\n transform: translateX(100%);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n }\n\n @keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n }\n\n .node-controls-panel.touch-optimized button {\n min-height: 56px;\n font-size: 1.125rem;\n }\n\n .node-controls-panel.touch-optimized input {\n min-height: 56px;\n font-size: 1.125rem;\n }\n `}</style>\n </>\n );\n};\n","/* eslint-disable react-hooks/set-state-in-effect */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport React, { useState, useEffect } from 'react';\nimport type { DeviceControlBinding } from '../../diagram/schema/DeviceControlBinding';\nimport { resolveStatusColor, normalizeDeviceStatus, FORCED_MARK_COLOR } from '../../theme/statusColors';\nimport {\n UnknownStatusGlyph,\n UNKNOWN_GLYPH_FILL_STYLE,\n unknownGlyphHostStyle,\n} from '../../theme/unknownStatusGlyph';\n\n/**\n * Disabled START / STOP look (alphinity-tffi #3853). The hatch and the dashed\n * border carry \"disabled\" without colour, as ISA-101 requires.\n */\nconst DISABLED_BUTTON_BG = '#e5e7eb';\nconst DISABLED_BUTTON_TEXT = '#4b5563';\nconst DISABLED_BUTTON_BORDER = '#6b7280';\nconst DISABLED_BUTTON_HATCH =\n 'repeating-linear-gradient(135deg, rgba(0,0,0,0.10) 0px, rgba(0,0,0,0.10) 4px, transparent 4px, transparent 10px)';\n\n/** START and STOP fill colours. The ramp `start` variant shares START's green. */\nconst START_BUTTON_BG = '#10b981';\nconst STOP_BUTTON_BG = '#ef4444';\n\n/**\n * Enabled look for the command variants of a custom action (ramp buttons).\n * Red stays only on STOP, so `cancel` is slate grey.\n */\nconst COMMAND_VARIANT_STYLES = {\n start: { backgroundColor: START_BUTTON_BG, color: '#ffffff' },\n hold: { backgroundColor: '#f59e0b', color: '#1f1300' },\n cancel: { backgroundColor: '#64748b', color: '#ffffff' },\n} as const;\n\n/** The `complete` variant: a status, so a solid border and no hatch. */\nconst COMPLETE_VARIANT_STYLE: React.CSSProperties = {\n backgroundColor: '#d1fae5',\n color: '#065f46',\n border: `2px solid ${START_BUTTON_BG}`,\n cursor: 'default',\n opacity: 1,\n};\n\nexport interface DeviceControlPanelProps {\n /** Control binding to display */\n binding: DeviceControlBinding | null;\n /** Called when panel should close */\n onClose: () => void;\n /** Panel position */\n position?: 'bottom' | 'side';\n /** Horizontal alignment for bottom position (default: center) */\n align?: 'left' | 'center' | 'right';\n /** Optimize for touch (larger targets) */\n touchOptimized?: boolean;\n /** Compact mode - reduces padding and spacing */\n compact?: boolean;\n /** Font size scale (default: 1.0, smaller: 0.8, larger: 1.2) */\n fontScale?: number;\n /** Maximum height for bottom position (default: 70vh) */\n maxHeight?: string;\n /** Maximum width for bottom position (default: none) */\n maxWidth?: string;\n /** Toast notification duration in milliseconds (default: 3000) */\n toastDuration?: number;\n /** Called when mode changes */\n onModeChange?: (mode: string) => void;\n /** Called when parameter changes */\n onParameterChange?: (parameterId: string, value: any) => void;\n /**\n * Called when the operator activates (taps) a numeric/text parameter's value.\n * When provided, that parameter renders as a tappable value instead of an\n * inline input, so the consumer can open its own editor (e.g. a numpad dialog)\n * and commit via `onParameterChange`. Read-only params are never activatable.\n * When omitted, parameters use the default inline-edit behavior.\n */\n onParameterActivate?: (parameterId: string, value: number | string | boolean) => void;\n /** Called when start button clicked */\n onStart?: () => void;\n /** Called when stop button clicked */\n onStop?: () => void;\n /**\n * Start command is in flight (awaiting ack). START shows \"STARTING...\" and\n * is disabled, so a second tap cannot send a second start. STOP stays\n * enabled: a pending start never disables STOP (alphinity-tffi #3853).\n * Wire from the hook's `isCommandPending(nodeId, 'start')`, or from the\n * host's own in-flight state.\n */\n startPending?: boolean;\n /**\n * Stop command is in flight (awaiting ack). STOP shows \"STOPPING...\" and\n * stays enabled, so STOP is always one tap. Only `capabilities.canStop`\n * or `capabilities.stopEnabled` can disable STOP.\n */\n stopPending?: boolean;\n /** Called when custom action clicked */\n onCustomAction?: (actionId: string) => void;\n /** Called when config button clicked (opens config panel for this device) */\n onOpenConfig?: () => void;\n /** Show configuration button in header */\n showConfigButton?: boolean;\n /** Called when undock button clicked (optional, shows undock button if provided) */\n onUndock?: () => void;\n /** Icon to display in undock button (default: '⇱') */\n undockIcon?: string;\n /** Style for mode selector: 'buttons' (horizontal scroll), 'dropdown' (select), or 'modal' (click to open modal) */\n modeSelectionStyle?: 'buttons' | 'dropdown' | 'modal';\n /**\n * Render this panel's OWN result toast. Default true, so existing\n * consumers are unchanged.\n *\n * Set false when the host application already surfaces command and save\n * results through its own notification channel. Without it the operator\n * sees two notices for one action - this panel's optimistic \"command\n * sent\", in this panel's styling, followed by the host's message for the\n * real outcome - which reads as two different mechanisms.\n *\n * The result still lands in `binding.state`, so a host that opts out can\n * read it and render its own.\n */\n showBuiltInToast?: boolean;\n /** Embedded mode - uses static positioning for rendering in containers (default: false) */\n embedded?: boolean;\n}\n\n/**\n * Memoized below (kiosk heap-leak fix, same rationale as DeviceConfigPanel):\n * an OPEN control panel must not re-render on host ticks whose props did not\n * change. Unlike the config panel, a control binding legitimately changes when\n * its live values move, so the memo bails only on genuinely idle ticks - the\n * win is proportional to how often values are flat at display precision.\n */\nfunction DeviceControlPanelImpl({\n binding,\n onClose,\n position = 'bottom',\n align = 'center',\n touchOptimized = true,\n compact = false,\n fontScale = 1.0,\n maxHeight,\n maxWidth,\n toastDuration = 3000,\n onModeChange,\n onParameterChange,\n onParameterActivate,\n onStart,\n onStop,\n startPending = false,\n stopPending = false,\n onCustomAction,\n onOpenConfig,\n showConfigButton = true,\n onUndock,\n undockIcon = '⇱',\n modeSelectionStyle = 'buttons',\n showBuiltInToast = true,\n embedded = false,\n}: DeviceControlPanelProps) {\n const [localParameterValues, setLocalParameterValues] = useState<Record<string, any>>({});\n const [showModeModal, setShowModeModal] = useState(false);\n const [pendingMode, setPendingMode] = useState<string | null>(null);\n const [showToast, setShowToast] = useState(true);\n\n // Sync local parameter values with binding\n useEffect(() => {\n if (binding) {\n const currentParams = binding.parameters[binding.state.currentMode] || [];\n const values: Record<string, any> = {};\n currentParams.forEach((param) => {\n values[param.id] = param.value;\n });\n setLocalParameterValues(values);\n }\n }, [binding, binding?.state.currentMode]);\n\n // Auto-dismiss toast after configured duration\n useEffect(() => {\n if (binding?.state.lastCommandResult) {\n setShowToast(true);\n const timer = setTimeout(() => {\n setShowToast(false);\n }, toastDuration);\n return () => clearTimeout(timer);\n }\n }, [binding?.state.lastCommandResult, toastDuration]);\n\n if (!binding) return null;\n\n const currentMode = binding.state.currentMode;\n const currentParams = binding.parameters[currentMode] || [];\n const touchSize = touchOptimized ? 56 : 40;\n\n // Spacing and sizing based on compact mode\n const spacing = {\n padding: compact ? '12px' : '20px',\n gap: compact ? '6px' : '8px',\n marginBottom: compact ? '10px' : '16px',\n itemPadding: compact ? '8px 10px' : '10px 12px',\n };\n\n // Font sizes with scale applied\n const fontSize = {\n title: Math.round(14 * fontScale),\n label: Math.round(11 * fontScale),\n value: Math.round(13 * fontScale),\n input: Math.round(14 * fontScale),\n button: Math.round(12 * fontScale),\n status: Math.round(10 * fontScale),\n description: Math.round(13 * fontScale),\n };\n\n // Get current mode object to check for mode-specific capabilities\n const currentModeObj = binding.modes.find((m) => m.value === currentMode);\n\n // Use mode-specific capabilities if defined, otherwise fall back to binding-level capabilities\n const activeCapabilities = currentModeObj?.capabilities ?? binding.capabilities ?? {};\n\n // ISA-101 High Performance HMI colors - matched to telemetry theme\n // Standard: Gray backgrounds, minimal color except for alarms\n const backgroundColor = binding.theme?.backgroundColor || '#edeeef'; // Match telemetry background\n const surfaceColor = '#f5f6f7'; // Slightly lighter than background\n const borderColor = '#d2d2d2'; // Match canvas background\n const textColor = binding.theme?.textColor || '#000000'; // Black text\n const mutedTextColor = '#666666'; // Muted text\n const selectedColor = '#b4d0fe'; // Light blue accent (match trendlines)\n\n // ISA-101: Resolve status color from shared defaults, with per-binding theme overrides\n const statusEntry = resolveStatusColor(binding.state.status, binding.theme?.statusColors as any);\n const statusColor = statusEntry.bg;\n\n // An untrusted read carries a marker as well as a colour, because colour\n // alone cannot separate every status.\n const isUnknownStatus = normalizeDeviceStatus(binding.state.status) === 'unknown';\n\n // Fail open on telemetry: this panel never reads telemetry running-state\n // (which can lag) to disable START or STOP. A wrongly-disabled button locks\n // the operator out, and that costs more than a wrongly-enabled one.\n //\n // Fixed places (alphinity-tffi #3853): when the device has START or STOP,\n // the row shows BOTH buttons, each in its own place. A button that does not\n // apply is disabled. It is never removed, and the other button never takes\n // its place. Before #3853, START turned into STOP in the same place, so a\n // second tap (a double tap with gloves, or a tap again after a slow\n // response) stopped the pump that the first tap started.\n //\n // A button is disabled only when:\n // - the consumer says the command does not apply (`canStart` /\n // `canStop` false, or `startEnabled` / `stopEnabled` false), or\n // - START only: a start command is in flight (`startPending`).\n // STOP is never disabled by a pending command, so STOP is always one tap.\n // A consumer that passes none of these keeps both buttons enabled.\n const startStopRowShown =\n binding.display?.showStartStop !== false &&\n (activeCapabilities?.canStart === true || activeCapabilities?.canStop === true);\n // Custom actions in ONE fixed row (no wrap, no 120px minimum) when the\n // consumer asks for it; any other value keeps the default wrap layout.\n const customActionsInRow = binding.display?.customActionsLayout === 'row';\n const startEnabledNow =\n activeCapabilities?.canStart === true &&\n activeCapabilities?.startEnabled !== false &&\n !startPending;\n const stopEnabledNow =\n activeCapabilities?.canStop === true && activeCapabilities?.stopEnabled !== false;\n // Touch-first type: the START / STOP label is never smaller than 18px.\n const startStopFontPx = Math.max(18, Math.round(18 * fontScale));\n const startStopButtonStyle = (enabled: boolean, enabledBg: string): React.CSSProperties => ({\n flex: 1,\n minWidth: 0,\n height: Math.max(touchSize, 56),\n borderRadius: '6px',\n fontSize: `${startStopFontPx}px`,\n fontWeight: 700,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n ...(enabled\n ? {\n backgroundColor: enabledBg,\n color: '#ffffff',\n border: '2px solid transparent',\n cursor: 'pointer',\n opacity: 1,\n }\n : {\n // Disabled look that does not depend on colour (ISA-101): a dashed\n // border and a diagonal hatch. A grey fill alone is not enough.\n backgroundColor: DISABLED_BUTTON_BG,\n backgroundImage: DISABLED_BUTTON_HATCH,\n color: DISABLED_BUTTON_TEXT,\n border: `2px dashed ${DISABLED_BUTTON_BORDER}`,\n cursor: 'not-allowed',\n opacity: 1,\n }),\n });\n\n const handleModeChange = (mode: string) => {\n onModeChange?.(mode);\n };\n\n const handleParameterCommit = (parameterId: string, value: any) => {\n onParameterChange?.(parameterId, value);\n };\n\n const handleParameterInput = (parameterId: string, value: any) => {\n setLocalParameterValues((prev) => ({ ...prev, [parameterId]: value }));\n };\n\n // Panel styles\n const panelStyle: React.CSSProperties = embedded\n ? {\n // Embedded mode: static positioning for container layouts\n position: 'static',\n width: '100%',\n backgroundColor,\n color: textColor,\n borderRadius: '8px 8px 0 0',\n boxShadow: 'none',\n border: `2px solid ${borderColor}`,\n padding: spacing.padding,\n maxHeight: maxHeight || '70vh',\n overflowY: 'auto',\n ...binding.display?.style,\n }\n : {\n // Fixed mode: traditional overlay positioning\n position: 'fixed',\n ...(position === 'bottom'\n ? {\n bottom: 0,\n ...(align === 'left'\n ? { left: 0, right: 'auto', width: maxWidth }\n : align === 'right'\n ? { right: 0, left: 'auto', width: maxWidth }\n : {\n left: 0,\n right: 0,\n marginLeft: 'auto',\n marginRight: 'auto',\n maxWidth: maxWidth,\n }),\n animation: 'slideUp 0.3s ease-out',\n }\n : {\n right: 0,\n top: 0,\n bottom: 0,\n width: maxWidth || '400px',\n animation: 'slideIn 0.3s ease-out',\n }),\n backgroundColor,\n color: textColor,\n borderRadius: position === 'bottom' ? '8px 8px 0 0' : '8px 0 0 8px',\n boxShadow: 'none',\n border: `2px solid ${borderColor}`,\n padding: spacing.padding,\n zIndex: 9999,\n maxHeight: maxHeight || (position === 'bottom' ? '70vh' : '100vh'),\n overflowY: 'auto',\n ...binding.display?.style,\n };\n\n const titleText = binding.display?.title || binding.deviceName || binding.tag || binding.nodeId;\n\n return (\n <>\n {/* Panel */}\n <div style={panelStyle} className={binding.display?.className}>\n {/* Header */}\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: '16px',\n paddingBottom: '12px',\n borderBottom: `2px solid ${borderColor}`,\n }}\n >\n <div>\n <h3\n style={{\n margin: 0,\n fontSize: `${fontSize.title}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n color: textColor,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n maxWidth: '200px',\n }}\n title={titleText.length > 25 ? titleText : undefined}\n >\n {titleText.length > 25 ? titleText.slice(0, 25) + '...' : titleText}\n </h3>\n {binding.deviceType && (\n <div\n style={{\n fontSize: '11px',\n color: mutedTextColor,\n marginTop: '4px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {binding.deviceType}\n </div>\n )}\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n {/* Config button */}\n {showConfigButton && onOpenConfig && (\n <button\n onClick={onOpenConfig}\n title=\"Open Configuration\"\n style={{\n width: touchSize * 0.7,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: '16px',\n cursor: 'pointer',\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n padding: '0',\n }}\n >\n ⚙\n </button>\n )}\n {/* Undock button */}\n {onUndock && (\n <button\n onClick={onUndock}\n title=\"Undock panel\"\n style={{\n width: touchSize * 0.7,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: '16px',\n cursor: 'pointer',\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n padding: '0',\n }}\n >\n {undockIcon}\n </button>\n )}\n {/* Close button */}\n <button\n onClick={onClose}\n style={{\n width: touchSize * 0.7,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: '18px',\n cursor: 'pointer',\n fontWeight: 'bold',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n ×\n </button>\n </div>\n </div>\n\n {/* Status Indicator */}\n {binding.state.status && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n padding: spacing.itemPadding,\n backgroundColor: surfaceColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n marginBottom: spacing.marginBottom,\n }}\n >\n <div\n data-testid=\"device-status-dot\"\n style={{\n width: '10px',\n height: '10px',\n borderRadius: '50%',\n backgroundColor: statusColor,\n border: '1px solid #333',\n // The shared helper positions this dot only while it shows\n // the unknown marker. The glyph then fills the dot with an\n // absolute inset, the same pattern StatusIndicator uses.\n ...unknownGlyphHostStyle(isUnknownStatus),\n }}\n >\n {isUnknownStatus && (\n <UnknownStatusGlyph size=\"small\" style={UNKNOWN_GLYPH_FILL_STYLE} />\n )}\n </div>\n <div style={{ flex: 1 }}>\n <div\n style={{\n fontWeight: 600,\n textTransform: 'uppercase',\n fontSize: `${fontSize.label}px`,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n color: textColor,\n }}\n >\n {binding.state.status}\n </div>\n {binding.state.statusMessage && (\n <div\n style={{\n fontSize: '10px',\n color: mutedTextColor,\n marginTop: '2px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {binding.state.statusMessage}\n </div>\n )}\n </div>\n {binding.state.isRunning !== undefined && (\n <div\n style={{\n padding: '4px 10px',\n // This badge shows the last published isRunning value. It\n // never re-derives that value here. When status is\n // unknown, the value may be stale. Muted colors carry that\n // warning alongside the word, so color is not the only\n // channel and the wording is not the only channel either.\n backgroundColor: isUnknownStatus\n ? statusEntry.bg\n : binding.state.isRunning\n ? selectedColor\n : surfaceColor,\n border: `1px solid ${isUnknownStatus ? statusEntry.border : borderColor}`,\n borderRadius: '2px',\n fontSize: '10px',\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n color: isUnknownStatus ? statusEntry.text : textColor,\n }}\n >\n {binding.state.isRunning ? 'RUNNING' : 'STOPPED'}\n {isUnknownStatus && ' (unconfirmed)'}\n </div>\n )}\n </div>\n )}\n\n {/* Mode Selector */}\n {binding.display?.showModeSelector !== false && binding.modes.length > 0 && (\n <div style={{ marginBottom: '16px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '8px',\n fontSize: '11px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n fontFamily: 'Arial, sans-serif',\n color: mutedTextColor,\n }}\n >\n Mode\n </label>\n {modeSelectionStyle === 'dropdown' ? (\n <select\n value={currentMode}\n onChange={(e) => handleModeChange(e.target.value)}\n style={{\n width: '100%',\n height: touchSize,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n paddingLeft: '12px',\n paddingRight: '12px',\n cursor: 'pointer',\n }}\n >\n {binding.modes.map((mode) => (\n <option key={mode.value} value={mode.value} disabled={mode.enabled === false}>\n {mode.label}\n </option>\n ))}\n </select>\n ) : modeSelectionStyle === 'modal' ? (\n <>\n <div\n onClick={() => {\n setPendingMode(currentMode);\n setShowModeModal(true);\n }}\n style={{\n padding: '12px',\n backgroundColor: surfaceColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n cursor: 'pointer',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <span>\n {binding.modes.find((m) => m.value === currentMode)?.label || currentMode}\n </span>\n <span style={{ fontSize: '20px', marginLeft: '8px' }}>▼</span>\n </div>\n {showModeModal && (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.5)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 20000,\n }}\n onClick={() => {\n setShowModeModal(false);\n setPendingMode(null);\n }}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n style={{\n backgroundColor: backgroundColor,\n borderRadius: '8px',\n padding: '24px',\n minWidth: '300px',\n maxWidth: '400px',\n border: `2px solid ${borderColor}`,\n }}\n >\n <h3\n style={{\n margin: '0 0 4px 0',\n fontSize: `${fontSize.title}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n color: textColor,\n }}\n >\n Select Mode\n </h3>\n <div\n style={{\n marginBottom: '16px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n color: mutedTextColor,\n textTransform: 'uppercase',\n }}\n >\n {binding.display?.title ||\n binding.deviceName ||\n binding.tag ||\n binding.nodeId}\n </div>\n <select\n value={pendingMode || currentMode}\n onChange={(e) => setPendingMode(e.target.value)}\n style={{\n width: '100%',\n height: `${touchSize}px`,\n marginBottom: '8px',\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n paddingLeft: '12px',\n paddingRight: '12px',\n cursor: 'pointer',\n }}\n >\n {binding.modes.map((mode) => (\n <option\n key={mode.value}\n value={mode.value}\n disabled={mode.enabled === false}\n >\n {mode.label}\n </option>\n ))}\n </select>\n {(() => {\n const selectedMode = binding.modes.find(\n (m) => m.value === (pendingMode || currentMode)\n );\n return selectedMode?.description ? (\n <div\n style={{\n marginBottom: '16px',\n padding: '12px',\n backgroundColor: surfaceColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.description}px`,\n color: mutedTextColor,\n fontFamily: 'Arial, sans-serif',\n lineHeight: '1.4',\n }}\n >\n {selectedMode.description}\n </div>\n ) : null;\n })()}\n <div style={{ display: 'flex', gap: '12px' }}>\n <button\n onClick={() => {\n // Always send the displayed mode on confirm — either the\n // user's explicit selection (pendingMode) or the current\n // dropdown value (currentMode / first available mode).\n // This lets operators re-issue the same command without\n // toggling away and back.\n const modeToSend =\n pendingMode || currentMode || binding.modes[0]?.value;\n if (modeToSend) {\n handleModeChange(modeToSend);\n }\n setShowModeModal(false);\n setPendingMode(null);\n }}\n style={{\n flex: 1,\n height: `${touchSize}px`,\n backgroundColor: '#4ade80',\n color: '#000000',\n border: '2px solid #22c55e',\n borderRadius: '6px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n cursor: 'pointer',\n }}\n >\n Confirm\n </button>\n <button\n onClick={() => {\n setShowModeModal(false);\n setPendingMode(null);\n }}\n style={{\n flex: 1,\n height: `${touchSize}px`,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n cursor: 'pointer',\n }}\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n )}\n </>\n ) : (\n <div\n style={{\n display: 'flex',\n gap: spacing.gap,\n overflowX: 'auto',\n overflowY: 'hidden',\n scrollbarWidth: 'thin',\n scrollbarColor: `${borderColor} transparent`,\n paddingBottom: '4px',\n marginBottom: '-4px',\n }}\n >\n {binding.modes.map((mode) => {\n const isSelected = mode.value === currentMode;\n const isEnabled = mode.enabled !== false;\n\n return (\n <button\n key={mode.value}\n onClick={() => isEnabled && handleModeChange(mode.value)}\n disabled={!isEnabled}\n style={{\n flex: '0 0 auto',\n minWidth: '90px',\n height: touchSize,\n backgroundColor: isSelected ? selectedColor : surfaceColor,\n color: isSelected ? '#000000' : textColor,\n border: `1px solid ${isSelected ? '#b4d0fe' : borderColor}`,\n borderRadius: '6px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n cursor: isEnabled ? 'pointer' : 'not-allowed',\n opacity: isEnabled ? 1 : 0.4,\n whiteSpace: 'nowrap',\n }}\n >\n {mode.label}\n </button>\n );\n })}\n </div>\n )}\n </div>\n )}\n\n {/* Parameters */}\n {currentParams.length > 0 && (\n <div style={{ marginBottom: spacing.marginBottom }}>\n <label\n style={{\n display: 'block',\n marginBottom: spacing.gap,\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n fontFamily: 'Arial, sans-serif',\n color: mutedTextColor,\n }}\n >\n Parameters\n </label>\n {currentParams.map((param) => (\n <div\n key={param.id}\n style={{\n marginBottom: spacing.gap,\n padding: spacing.itemPadding,\n backgroundColor: surfaceColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n }}\n >\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n marginBottom: spacing.gap,\n }}\n >\n <span\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n fontFamily: 'Arial, sans-serif',\n color: mutedTextColor,\n }}\n >\n {param.label}\n {param.forced?.active && (\n <span\n aria-label=\"Forced\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '16px',\n height: '16px',\n fontSize: '10px',\n fontWeight: 700,\n color: FORCED_MARK_COLOR.text,\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n borderRadius: '50%',\n }}\n >\n F\n </span>\n )}\n </span>\n <span\n style={{\n fontSize: `${fontSize.value}px`,\n fontWeight: 700,\n fontFamily: 'Arial, sans-serif',\n color: textColor,\n }}\n >\n {localParameterValues[param.id] ?? param.value}{' '}\n {param.unit && (\n <span style={{ color: mutedTextColor, fontSize: '10px', fontWeight: 600 }}>\n {param.unit}\n </span>\n )}\n </span>\n </div>\n {param.forced?.active && param.forced.realValue !== undefined && (\n <div\n style={{\n display: 'flex',\n justifyContent: 'flex-end',\n marginTop: '-4px',\n marginBottom: spacing.gap,\n fontSize: '10px',\n fontWeight: 500,\n fontFamily: 'Arial, sans-serif',\n color: mutedTextColor,\n }}\n >\n {/* A discrete output's real value is a boolean (R2: open/closed,\n on/off); render it as ON/OFF, matching DataOverlay's\n formatValue, not the literal string \"true\"/\"false\". */}\n REAL{' '}\n {typeof param.forced.realValue === 'boolean'\n ? param.forced.realValue\n ? 'ON'\n : 'OFF'\n : String(param.forced.realValue)}\n {param.unit ? ` ${param.unit}` : ''}\n </div>\n )}\n\n {param.type === 'boolean' ? (\n <button\n onClick={() => {\n const newValue = !localParameterValues[param.id];\n handleParameterInput(param.id, newValue);\n handleParameterCommit(param.id, newValue);\n }}\n disabled={param.readOnly}\n style={{\n width: '100%',\n height: touchSize,\n backgroundColor: localParameterValues[param.id] ? '#3f3f46' : surfaceColor,\n color: localParameterValues[param.id] ? textColor : mutedTextColor,\n border: `1px solid ${localParameterValues[param.id] ? '#52525b' : borderColor}`,\n borderRadius: '4px',\n fontSize: '12px',\n fontWeight: 600,\n fontFamily: 'monospace',\n letterSpacing: '0.5px',\n cursor: param.readOnly ? 'not-allowed' : 'pointer',\n transition: 'all 0.15s',\n }}\n >\n {localParameterValues[param.id] ? 'ON' : 'OFF'}\n </button>\n ) : param.type === 'select' ? (\n <select\n value={localParameterValues[param.id] || param.value}\n onChange={(e) => {\n handleParameterInput(param.id, e.target.value);\n handleParameterCommit(param.id, e.target.value);\n }}\n disabled={param.readOnly}\n style={{\n width: '100%',\n height: touchSize,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '4px',\n padding: '0 12px',\n fontSize: '12px',\n fontFamily: 'monospace',\n cursor: 'pointer',\n }}\n >\n {param.options?.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n ) : onParameterActivate && !param.readOnly ? (\n // Tappable value: defer editing to the consumer's editor (e.g. a\n // numpad dialog). Commit happens via onParameterChange.\n <button\n type=\"button\"\n onClick={() =>\n onParameterActivate(param.id, localParameterValues[param.id] ?? param.value)\n }\n style={{\n width: '100%',\n height: touchSize,\n display: 'flex',\n alignItems: 'center',\n backgroundColor: '#ffffff',\n color: textColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n padding: '0 12px',\n fontSize: `${fontSize.input}px`,\n fontFamily: 'Arial, sans-serif',\n fontWeight: 600,\n textAlign: 'left',\n cursor: 'pointer',\n }}\n >\n {localParameterValues[param.id] ?? param.value}\n {param.unit ? ` ${param.unit}` : ''}\n </button>\n ) : (\n <input\n type={param.type === 'number' ? 'number' : 'text'}\n value={localParameterValues[param.id] ?? param.value}\n onChange={(e) =>\n handleParameterInput(\n param.id,\n param.type === 'number' ? +e.target.value : e.target.value\n )\n }\n onBlur={() => handleParameterCommit(param.id, localParameterValues[param.id])}\n disabled={param.readOnly}\n min={param.min}\n max={param.max}\n step={param.step}\n style={{\n width: '100%',\n height: touchSize,\n backgroundColor: '#ffffff',\n color: textColor,\n border: `1px solid ${borderColor}`,\n borderRadius: '6px',\n padding: '0 12px',\n fontSize: `${fontSize.input}px`,\n fontFamily: 'Arial, sans-serif',\n fontWeight: 600,\n }}\n />\n )}\n </div>\n ))}\n </div>\n )}\n\n {/* Start/Stop Buttons: two fixed places, see startStopRowShown */}\n {startStopRowShown && (\n <div\n data-testid=\"start-stop-row\"\n style={{ display: 'flex', gap: spacing.gap, marginBottom: spacing.marginBottom }}\n >\n <button\n type=\"button\"\n data-control=\"start\"\n data-enabled={startEnabledNow ? 'true' : 'false'}\n onClick={() => {\n if (startEnabledNow) onStart?.();\n }}\n disabled={!startEnabledNow}\n aria-busy={startPending || undefined}\n style={startStopButtonStyle(startEnabledNow, START_BUTTON_BG)}\n >\n {startPending ? 'STARTING\\u2026' : 'START'}\n </button>\n <button\n type=\"button\"\n data-control=\"stop\"\n data-enabled={stopEnabledNow ? 'true' : 'false'}\n onClick={() => {\n if (stopEnabledNow) onStop?.();\n }}\n disabled={!stopEnabledNow}\n aria-busy={stopPending || undefined}\n style={startStopButtonStyle(stopEnabledNow, STOP_BUTTON_BG)}\n >\n {stopPending ? 'STOPPING\\u2026' : 'STOP'}\n </button>\n </div>\n )}\n\n {/* Custom Actions */}\n {activeCapabilities?.customActions && activeCapabilities.customActions.length > 0 && (\n <div\n style={{\n display: 'flex',\n flexWrap: customActionsInRow ? 'nowrap' : 'wrap',\n gap: '8px',\n }}\n >\n {activeCapabilities.customActions.map((action) => {\n const isComplete = action.variant === 'complete';\n // COMPLETE is a status: the operator cannot press it.\n const actionDisabled = action.disabled === true || isComplete;\n const commandStyle =\n action.variant === 'start' || action.variant === 'hold' || action.variant === 'cancel'\n ? COMMAND_VARIANT_STYLES[action.variant]\n : undefined;\n let variantStyle: React.CSSProperties;\n if (isComplete) {\n variantStyle = COMPLETE_VARIANT_STYLE;\n } else if (commandStyle && actionDisabled) {\n // Same disabled look as START: hatch and dashed border (ISA-101).\n variantStyle = {\n backgroundColor: DISABLED_BUTTON_BG,\n backgroundImage: DISABLED_BUTTON_HATCH,\n color: DISABLED_BUTTON_TEXT,\n border: `2px dashed ${DISABLED_BUTTON_BORDER}`,\n cursor: 'not-allowed',\n opacity: 1,\n };\n } else if (commandStyle) {\n variantStyle = {\n ...commandStyle,\n border: '2px solid transparent',\n cursor: 'pointer',\n opacity: 1,\n };\n } else {\n variantStyle = {\n backgroundColor: surfaceColor,\n color: textColor,\n border: `1px solid ${borderColor}`,\n cursor: actionDisabled ? 'not-allowed' : 'pointer',\n opacity: actionDisabled ? 0.4 : 1,\n };\n }\n return (\n <button\n key={action.id}\n type=\"button\"\n data-variant={action.variant}\n onClick={() => {\n if (!actionDisabled) onCustomAction?.(action.id);\n }}\n disabled={actionDisabled}\n title={action.disabled === true ? action.disabledReason : undefined}\n style={{\n ...(customActionsInRow\n ? { flex: '1 1 0px', minWidth: 0, paddingLeft: '4px', paddingRight: '4px' }\n : { flex: 1, minWidth: '120px' }),\n height: touchSize,\n borderRadius: '6px',\n fontSize: '11px',\n fontWeight: 600,\n fontFamily: 'monospace',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n transition: 'all 0.15s',\n ...variantStyle,\n }}\n >\n {isComplete && !action.icon && (\n <span aria-hidden=\"true\" style={{ marginRight: '6px' }}>\n {'\\u2713'}\n </span>\n )}\n {action.icon && <span style={{ marginRight: '6px' }}>{action.icon}</span>}\n {action.label}\n </button>\n );\n })}\n </div>\n )}\n </div>\n\n {/* Toast Notification for Command Result */}\n {binding.state.lastCommandResult && showToast && showBuiltInToast && (\n <div\n style={{\n position: 'fixed',\n bottom: '20px',\n ...(position === 'bottom' && align === 'right'\n ? { left: '20px', right: 'auto' } // Show on left if panel is aligned right\n : { right: '20px', left: 'auto' }), // Default to right\n maxWidth: '400px',\n padding: '12px 16px',\n backgroundColor: binding.state.lastCommandResult.success ? '#10b981' : '#ef4444',\n color: '#ffffff',\n border: 'none',\n borderRadius: '8px',\n fontSize: `${fontSize.label}px`,\n fontFamily: 'Arial, sans-serif',\n fontWeight: 600,\n boxShadow: '0 4px 12px rgba(0,0,0,0.15)',\n zIndex: 10000,\n animation:\n position === 'bottom' && align === 'right'\n ? 'slideInToastLeft 0.3s ease-out'\n : 'slideInToast 0.3s ease-out',\n }}\n >\n {binding.state.lastCommandResult.message}\n </div>\n )}\n\n <style>{`\n @keyframes slideInToast {\n from { \n opacity: 0;\n transform: translateX(20px);\n }\n to { \n opacity: 1;\n transform: translateX(0);\n }\n }\n @keyframes slideInToastLeft {\n from { \n opacity: 0;\n transform: translateX(-20px);\n }\n to { \n opacity: 1;\n transform: translateX(0);\n }\n }\n @keyframes slideUp {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n }\n @keyframes slideIn {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n `}</style>\n </>\n );\n}\n\n/** Memo boundary - see DeviceControlPanelImpl doc comment (kiosk heap-leak fix). */\nexport const DeviceControlPanel = React.memo(DeviceControlPanelImpl);\n","import React, { useState, useEffect, useCallback, useRef, useId } from 'react';\n\nexport interface NumpadDialogProps {\n /** Whether the dialog is open */\n isOpen: boolean;\n /** Called when the dialog is dismissed without applying */\n onClose: () => void;\n /** Called with the validated, precision-rounded value when the user applies */\n onApply: (value: number) => void;\n /** Device / context name shown in the header */\n deviceName?: string;\n /** Parameter label shown in the header */\n parameterLabel?: string;\n /** Engineering unit shown next to the value and in range hints */\n unit?: string;\n /** Minimum allowed value (also enables the +/- key when negative) */\n min?: number;\n /** Maximum allowed value */\n max?: number;\n /** Step (reserved for future increment controls) */\n step?: number;\n /** Decimal precision applied on commit */\n precision?: number;\n /** Current value shown as a reference above the entry field */\n currentValue?: number;\n /**\n * Optimize for touch panels (larger keys/targets). Defaults to `true` because\n * this is aimed at industrial touchscreen HMIs; set `false` for compact\n * desktop/keyboard-first use.\n */\n touchOptimized?: boolean;\n}\n\n/**\n * Modal numeric keypad for entering a device setpoint.\n *\n * Pairs with `DeviceControlPanel`'s `onParameterActivate` hook: the panel\n * renders a tappable value, the consumer opens this dialog, and `onApply`\n * commits via `onParameterChange`.\n *\n * Designed for BOTH touch and keyboard:\n * - Touch: large keys, press feedback, backdrop dismiss, body scroll-lock.\n * - Keyboard: full typing (0-9, `.`, `-`, Backspace, Enter to apply, Escape to\n * cancel), on-screen keys kept out of the Tab order, focus trap, focus-visible\n * outlines.\n *\n * Safety for setpoint use: nothing auto-commits — only Apply / Enter writes, and\n * out-of-range values are rejected (never silently clamped). The field pre-fills\n * with the current value so an operator never blind-zeroes a live setpoint.\n */\nexport const NumpadDialog: React.FC<NumpadDialogProps> = ({\n isOpen,\n onClose,\n onApply,\n deviceName = '',\n parameterLabel = 'Setpoint',\n unit = '',\n min = 0,\n max = 100,\n precision,\n currentValue = 0,\n touchOptimized = true,\n}) => {\n const [inputValue, setInputValue] = useState('');\n // \"pristine\" = the field still holds the pre-filled current value, untouched.\n // The first keystroke replaces it (standard numpad behavior) so the operator\n // sees the current setpoint on open but can type a fresh value immediately.\n const [pristine, setPristine] = useState(true);\n const dialogRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n const decimals = precision ?? 1;\n\n // On open, pre-fill with the current value (at the dialog's precision) so it's\n // the prominent number and matches what Apply would commit.\n useEffect(() => {\n if (isOpen) {\n setInputValue(Number(currentValue).toFixed(decimals));\n setPristine(true);\n }\n }, [isOpen, currentValue, decimals]);\n\n // Move focus into the dialog on open and restore it to the trigger on close.\n useEffect(() => {\n if (!isOpen) return;\n const previouslyFocused = document.activeElement as HTMLElement | null;\n dialogRef.current?.focus();\n return () => previouslyFocused?.focus?.();\n }, [isOpen]);\n\n // Lock background scroll so a touch drag can't scroll the diagram behind us.\n useEffect(() => {\n if (!isOpen) return;\n const prevOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n return () => {\n document.body.style.overflow = prevOverflow;\n };\n }, [isOpen]);\n\n const handleApply = useCallback(() => {\n const n = Number(inputValue);\n if (inputValue === '' || inputValue === '-' || inputValue === '.' || inputValue === '-.') return;\n if (isNaN(n) || n < min || n > max) return;\n onApply(Number(n.toFixed(decimals)));\n }, [inputValue, min, max, onApply, decimals]);\n\n const appendDigit = useCallback(\n (digit: string) => {\n setInputValue((prev) => {\n // First keystroke after open replaces the pre-filled current value.\n const base = pristine ? '' : prev;\n if (digit === '.') {\n // No decimal point allowed when precision is 0 (integer-only).\n if (decimals === 0) return base;\n if (base.includes('.')) return base;\n if (base === '' || base === '-') return base + '0.';\n return base + '.';\n }\n // Cap decimal places at `precision` so the on-screen value is exactly\n // what Apply commits — no surprise rounding/truncation on commit.\n const dot = base.indexOf('.');\n if (dot !== -1 && base.length - dot - 1 >= decimals) return base;\n // Replace a lone leading zero (except when starting a decimal)\n if (base === '0') return digit;\n if (base === '-0') return '-' + digit;\n return base + digit;\n });\n if (pristine) setPristine(false);\n },\n [pristine, decimals]\n );\n\n const handleBackspace = useCallback(() => {\n setPristine(false);\n setInputValue((prev) => prev.slice(0, -1));\n }, []);\n\n const handleClear = useCallback(() => {\n setPristine(false);\n setInputValue('');\n }, []);\n\n const handleToggleSign = useCallback(() => {\n if (min >= 0) return;\n setPristine(false);\n setInputValue((prev) => {\n if (prev.startsWith('-')) return prev.slice(1);\n if (prev === '' || prev === '0') return '-';\n return '-' + prev;\n });\n }, [min]);\n\n // Long-press Backspace to clear (touch convenience). A short tap backspaces;\n // holding ~500ms clears the whole entry.\n const bsTimer = useRef<number | null>(null);\n const bsLongFired = useRef(false);\n const clearBsTimer = () => {\n if (bsTimer.current !== null) {\n clearTimeout(bsTimer.current);\n bsTimer.current = null;\n }\n };\n const handleBackspacePointerDown = () => {\n bsLongFired.current = false;\n bsTimer.current = window.setTimeout(() => {\n bsLongFired.current = true;\n handleClear();\n }, 500);\n };\n const handleBackspaceClick = () => {\n clearBsTimer();\n if (bsLongFired.current) {\n bsLongFired.current = false;\n return; // long-press already cleared; don't also backspace\n }\n handleBackspace();\n };\n useEffect(() => () => clearBsTimer(), []);\n\n // Keyboard support + focus trap + Enter-guard.\n useEffect(() => {\n if (!isOpen) return;\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Tab') {\n const root = dialogRef.current;\n if (!root) return;\n // On-screen keys are tabIndex=-1, so only the actions (Cancel/Apply) are\n // tabbable. Cycle focus within the dialog.\n const focusable = Array.from(\n root.querySelectorAll<HTMLElement>('button:not([disabled])')\n ).filter((el) => el.tabIndex !== -1);\n if (focusable.length === 0) {\n e.preventDefault();\n root.focus();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement as HTMLElement | null;\n if (e.shiftKey) {\n if (active === first || !root.contains(active)) {\n e.preventDefault();\n last.focus();\n }\n } else if (active === last || !root.contains(active)) {\n e.preventDefault();\n first.focus();\n }\n return;\n }\n if (e.key === 'Enter') {\n // If a button is focused, let its native click handle Enter (avoids a\n // double Apply/Cancel). Otherwise apply.\n const active = document.activeElement as HTMLElement | null;\n if (active && active.tagName === 'BUTTON' && dialogRef.current?.contains(active)) return;\n e.preventDefault();\n handleApply();\n } else if (e.key === 'Escape') {\n e.preventDefault();\n onClose();\n } else if (e.key === 'Backspace') {\n e.preventDefault();\n handleBackspace();\n } else if (/^[0-9]$/.test(e.key)) {\n e.preventDefault();\n appendDigit(e.key);\n } else if (e.key === '.') {\n e.preventDefault();\n appendDigit('.');\n } else if (e.key === '-') {\n e.preventDefault();\n handleToggleSign();\n }\n };\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [isOpen, handleApply, onClose, handleBackspace, appendDigit, handleToggleSign]);\n\n if (!isOpen) return null;\n\n const parsed = Number(inputValue);\n const hasNumber =\n inputValue !== '' &&\n inputValue !== '-' &&\n inputValue !== '.' &&\n inputValue !== '-.' &&\n !isNaN(parsed);\n // Live range check so the operator sees WHY a value is rejected while typing,\n // not only after pressing Apply.\n const belowMin = hasNumber && parsed < min;\n const aboveMax = hasNumber && parsed > max;\n const outOfRange = belowMin || aboveMax;\n const isValid = hasNumber && !outOfRange;\n\n const alarmColor = '#dc2626';\n const limitStyle = (violated: boolean): React.CSSProperties =>\n violated\n ? { color: alarmColor, fontWeight: 700, background: '#fee2e2', borderRadius: 3, padding: '0 3px' }\n : {};\n\n // Touch-first sizing.\n const keySize = touchOptimized ? 60 : 46;\n const keyFont = touchOptimized ? 22 : 18;\n const actionSize = touchOptimized ? 52 : 42;\n\n const keyStyle: React.CSSProperties = {\n minWidth: 52,\n minHeight: keySize,\n fontSize: keyFont,\n fontWeight: 600,\n borderRadius: 8,\n border: '1px solid #e2e8f0',\n background: '#ffffff',\n color: '#0f172a',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n userSelect: 'none',\n boxShadow: '0 1px 2px rgba(15, 23, 42, 0.06)',\n };\n\n const utilKeyStyle: React.CSSProperties = {\n flex: 1,\n minHeight: touchOptimized ? 44 : 38,\n fontSize: 13,\n fontWeight: 600,\n borderRadius: 8,\n border: '1px solid #e2e8f0',\n background: '#f8fafc',\n color: '#64748b',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n userSelect: 'none',\n };\n\n const reason = belowMin\n ? `Below minimum of ${min} ${unit}`\n : aboveMax\n ? `Above maximum of ${max} ${unit}`\n : 'Type or tap to change the value';\n\n // Backspace lives in the number grid (bottom-left) when negatives aren't\n // allowed; otherwise that cell is the +/- key and Backspace moves to the\n // utility row. Either way there is exactly one Backspace and no empty cell.\n const backspaceInGrid = min >= 0;\n const BackspaceButton = ({ style, label }: { style: React.CSSProperties; label: string }) => (\n <button\n type=\"button\"\n className=\"np-key\"\n tabIndex={-1}\n aria-label=\"Backspace\"\n style={style}\n onPointerDown={handleBackspacePointerDown}\n onPointerUp={clearBsTimer}\n onPointerLeave={clearBsTimer}\n onClick={handleBackspaceClick}\n >\n {label}\n </button>\n );\n\n return (\n // Backdrop\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(15, 23, 42, 0.4)',\n zIndex: 10000,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n onClick={onClose}\n >\n <style>{`\n .np-key { transition: background .1s, transform .05s; touch-action: manipulation; }\n .np-key:active:not(:disabled) { background:#e2e8f0; transform: translateY(1px); }\n .np-key:focus-visible, .np-action:focus-visible { outline: 3px solid #2563eb; outline-offset: 2px; }\n @keyframes np-blink { 0%,49%{opacity:1} 50%,100%{opacity:0} }\n .np-caret { display:inline-block; width:2px; height:1.1em; background:#2563eb; animation: np-blink 1s step-end infinite; }\n `}</style>\n {/* Modal */}\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n tabIndex={-1}\n style={{\n width: touchOptimized ? '340px' : '300px',\n maxHeight: '90vh',\n overflow: 'auto',\n background: '#ffffff',\n borderRadius: 12,\n border: '1px solid #e2e8f0',\n boxShadow: '0 10px 40px rgba(15, 23, 42, 0.2)',\n fontFamily: \"'Inter', system-ui, sans-serif\",\n outline: 'none',\n }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div style={{ padding: '14px 16px 0 16px' }}>\n <h3 id={titleId} style={{ margin: 0, fontSize: 15, fontWeight: 700, color: '#0f172a' }}>\n {deviceName ? `${deviceName} - ${parameterLabel}` : parameterLabel}\n </h3>\n </div>\n\n <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 10 }}>\n {/* Current value + range */}\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n fontSize: 12,\n color: '#64748b',\n }}\n >\n <span>\n Current: {currentValue} {unit}\n </span>\n <span>\n Range: <span style={limitStyle(belowMin)}>{min}</span>-\n <span style={limitStyle(aboveMax)}>{max}</span> {unit}\n </span>\n </div>\n\n {/* Input display — the entered value turns red when out of range */}\n <div\n aria-live=\"polite\"\n aria-atomic=\"true\"\n aria-label={`Entered value ${inputValue || '0'} ${unit}`}\n style={{\n background: outOfRange ? '#fef2f2' : '#f8fafc',\n borderRadius: 8,\n padding: '10px 14px',\n fontSize: 28,\n fontWeight: 700,\n textAlign: 'right',\n color: outOfRange ? alarmColor : '#0f172a',\n minHeight: 48,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n gap: 6,\n border: outOfRange ? '1.5px solid #fca5a5' : '1.5px solid #e2e8f0',\n }}\n >\n <span>{inputValue || '0'}</span>\n <span className=\"np-caret\" aria-hidden=\"true\" />\n <span style={{ fontSize: 14, color: '#94a3b8', fontWeight: 500 }}>{unit}</span>\n </div>\n\n {/* Reason / hint message (announced to screen readers) */}\n <div\n role=\"status\"\n aria-live=\"polite\"\n style={{\n fontSize: 12,\n color: outOfRange ? alarmColor : '#94a3b8',\n fontWeight: outOfRange ? 600 : 400,\n textAlign: 'right',\n minHeight: 16,\n }}\n >\n {reason}\n </div>\n\n {/* Numberpad — keys are out of the Tab order (keyboard users type digits) */}\n <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 5 }}>\n {['7', '8', '9', '4', '5', '6', '1', '2', '3'].map((digit) => (\n <button\n key={digit}\n type=\"button\"\n className=\"np-key\"\n tabIndex={-1}\n style={keyStyle}\n onClick={() => appendDigit(digit)}\n >\n {digit}\n </button>\n ))}\n {/* Bottom-left cell: +/- when negatives are allowed, else Backspace */}\n {min < 0 ? (\n <button\n type=\"button\"\n className=\"np-key\"\n tabIndex={-1}\n aria-label=\"Toggle sign\"\n style={{ ...keyStyle, fontSize: 16, background: '#f8fafc', color: '#64748b' }}\n onClick={handleToggleSign}\n >\n +/−\n </button>\n ) : (\n <BackspaceButton style={{ ...keyStyle, fontSize: 20, background: '#f8fafc', color: '#64748b' }} label=\"⌫\" />\n )}\n <button type=\"button\" className=\"np-key\" tabIndex={-1} style={keyStyle} onClick={() => appendDigit('0')}>\n 0\n </button>\n <button\n type=\"button\"\n className=\"np-key\"\n tabIndex={-1}\n aria-label=\"Decimal point\"\n style={{\n ...keyStyle,\n cursor: decimals === 0 ? 'not-allowed' : 'pointer',\n opacity: decimals === 0 ? 0.4 : 1,\n }}\n disabled={decimals === 0}\n onClick={() => appendDigit('.')}\n >\n .\n </button>\n </div>\n\n {/* Utility row: Clear (+ Backspace only when it's not in the grid) */}\n <div style={{ display: 'flex', gap: 5 }}>\n <button type=\"button\" className=\"np-key\" tabIndex={-1} style={utilKeyStyle} onClick={handleClear}>\n Clear\n </button>\n {!backspaceInGrid && <BackspaceButton style={utilKeyStyle} label=\"Backspace\" />}\n </div>\n\n {/* Action buttons (the only Tab stops in the dialog) */}\n <div style={{ display: 'flex', gap: 8, marginTop: 2 }}>\n <button\n type=\"button\"\n className=\"np-action\"\n onClick={onClose}\n style={{\n flex: 1,\n minHeight: actionSize,\n borderRadius: 8,\n border: '1px solid #e2e8f0',\n background: '#ffffff',\n color: '#0f172a',\n fontSize: 14,\n fontWeight: 600,\n cursor: 'pointer',\n }}\n >\n Cancel\n </button>\n <button\n type=\"button\"\n className=\"np-action\"\n onClick={handleApply}\n disabled={!isValid}\n aria-label={isValid ? `Apply ${inputValue} ${unit}` : 'Apply (enter a valid value)'}\n style={{\n flex: 1,\n minHeight: actionSize,\n borderRadius: 8,\n border: 'none',\n background: '#2563eb',\n color: '#ffffff',\n fontSize: 14,\n fontWeight: 600,\n cursor: isValid ? 'pointer' : 'not-allowed',\n opacity: isValid ? 1 : 0.4,\n }}\n >\n Apply\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n};\n","import React from 'react';\nimport { Tab } from '@mui/material';\nimport {\n ChevronLeft,\n ChevronRight,\n DisplayModeToggle,\n FixedSVGContainer,\n FooterPanel,\n FullscreenContainer,\n LeftPanel,\n LeftToggleButton,\n PanelContent,\n PanelTabs,\n RightPanel,\n RightToggleButton,\n SVGArea,\n ScrollableSVGWrapper,\n ZoomButton,\n ZoomControls,\n} from './FullscreenWorkspace.styles';\nimport type { DisplayMode, FullscreenWorkspaceProps } from './types';\n\nexport function FullscreenWorkspace({\n svgContent,\n overlays,\n leftPanel,\n rightPanel,\n footer,\n tabs,\n activeTab,\n onTabChange,\n defaultLeftCollapsed = false,\n defaultRightCollapsed = false,\n leftCollapsed: leftCollapsedProp,\n rightCollapsed: rightCollapsedProp,\n onLeftCollapseChange,\n onRightCollapseChange,\n displayMode,\n defaultDisplayMode = 'standard',\n onDisplayModeChange,\n showDisplayModeToggle = true,\n showZoomControls = false,\n onZoomIn,\n onZoomOut,\n onResetZoom,\n zoomDisabled = false,\n className = '',\n ...rest\n}: FullscreenWorkspaceProps): React.ReactElement {\n const [leftCollapsedState, setLeftCollapsedState] = React.useState(defaultLeftCollapsed);\n const [rightCollapsedState, setRightCollapsedState] = React.useState(defaultRightCollapsed);\n const [internalDisplayMode, setInternalDisplayMode] =\n React.useState<DisplayMode>(displayMode ?? defaultDisplayMode);\n const [internalTab, setInternalTab] = React.useState<string>(\n tabs && tabs.length > 0 ? tabs[0].value : ''\n );\n\n const leftCollapsed = leftCollapsedProp ?? leftCollapsedState;\n const rightCollapsed = rightCollapsedProp ?? rightCollapsedState;\n const isDisplayModeControlled = displayMode !== undefined;\n const currentDisplayMode = isDisplayModeControlled ? displayMode : internalDisplayMode;\n const isTabControlled = activeTab !== undefined && activeTab !== null;\n\n const resolvedTabs = React.useMemo(() => tabs ?? [], [tabs]);\n const defaultTabValue = resolvedTabs.length > 0 ? resolvedTabs[0].value : '';\n const currentTab = isTabControlled ? (activeTab as string) : internalTab || defaultTabValue;\n\n React.useEffect(() => {\n if (resolvedTabs.length > 0) {\n const values = resolvedTabs.map((tab) => tab.value);\n if (!values.includes(currentTab)) {\n const fallback = activeTab ?? defaultTabValue;\n setInternalTab(fallback);\n }\n }\n }, [resolvedTabs, activeTab, currentTab, defaultTabValue]);\n\n React.useEffect(() => {\n if (displayMode !== undefined) {\n setInternalDisplayMode(displayMode);\n }\n }, [displayMode]);\n\n const handleLeftToggle = () => {\n const next = !leftCollapsed;\n if (leftCollapsedProp === undefined) {\n setLeftCollapsedState(next);\n }\n onLeftCollapseChange?.(next);\n };\n\n const handleRightToggle = () => {\n const next = !rightCollapsed;\n if (rightCollapsedProp === undefined) {\n setRightCollapsedState(next);\n }\n onRightCollapseChange?.(next);\n };\n\n const handleModeToggle = () => {\n const nextMode = currentDisplayMode === 'standard' ? 'dashboard' : 'standard';\n if (!isDisplayModeControlled) {\n setInternalDisplayMode(nextMode);\n }\n onDisplayModeChange?.(nextMode);\n };\n\n const handleTabChangeInternal = (_: React.SyntheticEvent, value: string) => {\n if (!isTabControlled) {\n setInternalTab(value);\n }\n onTabChange?.(value);\n };\n\n const currentTabContent =\n resolvedTabs.find((tab) => tab.value === currentTab)?.content ?? null;\n\n const showLeftToggle = Boolean(leftPanel);\n const showRightToggle = Boolean(rightPanel);\n\n return (\n <FullscreenContainer\n leftCollapsed={leftCollapsed}\n rightCollapsed={rightCollapsed}\n className={`tff-svg-container ${className}`.trim()}\n {...rest}\n >\n {showLeftToggle && (\n <LeftToggleButton\n collapsed={leftCollapsed}\n onClick={handleLeftToggle}\n aria-label={leftCollapsed ? 'Expand left panel' : 'Collapse left panel'}\n >\n {leftCollapsed ? <ChevronRight /> : <ChevronLeft />}\n </LeftToggleButton>\n )}\n\n {showRightToggle && (\n <RightToggleButton\n collapsed={rightCollapsed}\n onClick={handleRightToggle}\n aria-label={rightCollapsed ? 'Expand right panel' : 'Collapse right panel'}\n >\n {rightCollapsed ? <ChevronLeft /> : <ChevronRight />}\n </RightToggleButton>\n )}\n\n {showDisplayModeToggle && (\n <DisplayModeToggle\n mode={currentDisplayMode}\n onClick={handleModeToggle}\n aria-label=\"Toggle display mode\"\n >\n {currentDisplayMode === 'dashboard' ? 'Dashboard Mode' : 'Standard Mode'}\n </DisplayModeToggle>\n )}\n\n <SVGArea>\n {showZoomControls && (\n <ZoomControls>\n <ZoomButton\n onClick={onZoomIn}\n disabled={zoomDisabled || !onZoomIn}\n size=\"small\"\n aria-label=\"Zoom in\"\n >\n +\n </ZoomButton>\n <ZoomButton\n onClick={onZoomOut}\n disabled={zoomDisabled || !onZoomOut}\n size=\"small\"\n aria-label=\"Zoom out\"\n >\n -\n </ZoomButton>\n {onResetZoom && (\n <ZoomButton\n onClick={onResetZoom}\n disabled={zoomDisabled}\n size=\"small\"\n aria-label=\"Reset zoom\"\n >\n Reset\n </ZoomButton>\n )}\n </ZoomControls>\n )}\n\n <ScrollableSVGWrapper className=\"svg-scroll-wrapper\">\n <FixedSVGContainer className=\"svg-container\">\n {svgContent}\n {overlays}\n </FixedSVGContainer>\n </ScrollableSVGWrapper>\n </SVGArea>\n\n <LeftPanel collapsed={leftCollapsed}>\n {resolvedTabs.length > 0 ? (\n <>\n <PanelTabs\n value={currentTab}\n onChange={handleTabChangeInternal}\n aria-label=\"Left panel tabs\"\n >\n {resolvedTabs.map((tab) => (\n <Tab key={tab.value} label={tab.label} value={tab.value} />\n ))}\n </PanelTabs>\n <PanelContent>{currentTabContent}</PanelContent>\n </>\n ) : (\n leftPanel\n )}\n </LeftPanel>\n\n <RightPanel collapsed={rightCollapsed}>{rightPanel}</RightPanel>\n\n <FooterPanel>{footer}</FooterPanel>\n </FullscreenContainer>\n );\n}\n\nexport type {\n DisplayMode,\n FullscreenContainerProps,\n FullscreenWorkspaceProps,\n FullscreenWorkspaceTab,\n OverlayTheme,\n SVGLockedOverlayProps,\n TrendLineProps,\n CircularGaugeProps,\n SparklineProps,\n EquipmentIndicatorProps,\n EquipmentIndicatorWithSparklineProps,\n} from './types';\n\nexport {\n FullscreenContainer,\n LeftPanel,\n RightPanel,\n SVGArea,\n LeftToggleButton,\n RightToggleButton,\n DisplayModeToggle,\n ZoomControls,\n ZoomButton,\n FooterPanel,\n PanelTabs,\n PanelContent,\n FixedSVGContainer,\n ScrollableSVGWrapper,\n ChevronLeft,\n ChevronRight,\n} from './FullscreenWorkspace.styles';\n\nexport {\n overlayStyles,\n SVGLockedOverlay,\n DashboardCard,\n CardHeader,\n CardValue,\n CardUnit,\n} from './FullscreenWorkspace.overlays';\n\nexport {\n TrendLine,\n Sparkline,\n CircularGauge,\n EquipmentIndicator,\n EquipmentIndicatorWithSparkline,\n} from './FullscreenWorkspace.visuals';\n","import { Box, IconButton, Tabs } from '@mui/material';\nimport styled from '@emotion/styled';\nimport type { DisplayMode, FullscreenContainerProps } from './types';\n\nexport const FullscreenContainer = styled(Box)<FullscreenContainerProps>(\n ({ leftCollapsed, rightCollapsed }) => {\n const getGridTemplateAreas = () => {\n if (leftCollapsed && rightCollapsed) {\n return `\"svg-area\" \"footer\"`;\n }\n if (leftCollapsed) {\n return `\"svg-area right-panel\" \"footer footer\"`;\n }\n if (rightCollapsed) {\n return `\"left-panel svg-area\" \"footer footer\"`;\n }\n return `\"left-panel svg-area right-panel\" \"footer footer footer\"`;\n };\n\n const getGridTemplateColumns = () => {\n if (leftCollapsed && rightCollapsed) {\n return '1fr';\n }\n if (leftCollapsed) {\n return '1fr 380px';\n }\n if (rightCollapsed) {\n return '420px 1fr';\n }\n return '420px 1fr 380px';\n };\n\n return {\n width: '100vw',\n height: '100vh',\n background: '#f5f5f5',\n display: 'grid',\n gridTemplateAreas: getGridTemplateAreas(),\n gridTemplateRows: '1fr 100px',\n gridTemplateColumns: getGridTemplateColumns(),\n gap: '10px',\n padding: '10px',\n boxSizing: 'border-box',\n transition: 'grid-template-columns 0.3s ease-in-out',\n };\n }\n);\n\nexport const LeftPanel = styled(Box)<{ collapsed: boolean }>(({ collapsed }) => ({\n gridArea: 'left-panel',\n background: 'rgba(255, 255, 255, 0.0)',\n border: '1px solid #ddd',\n borderRadius: '8px',\n padding: collapsed ? '0' : '15px',\n overflowY: 'auto',\n display: collapsed ? 'none' : 'block',\n transition: 'all 0.3s ease-in-out',\n}));\n\nexport const RightPanel = styled(Box)<{ collapsed: boolean }>(({ collapsed }) => ({\n gridArea: 'right-panel',\n background: 'rgba(255, 255, 255, 0.0)',\n border: '1px solid #ddd',\n borderRadius: '8px',\n padding: collapsed ? '0' : '15px',\n overflowY: 'auto',\n display: collapsed ? 'none' : 'block',\n transition: 'all 0.3s ease-in-out',\n}));\n\nexport const SVGArea = styled(Box)({\n gridArea: 'svg-area',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-start',\n position: 'relative',\n background: 'rgba(255, 255, 255, 0.0)',\n border: '1px solid #ddd',\n borderRadius: '8px',\n overflow: 'auto',\n width: '100%',\n height: '100%',\n});\n\nexport const LeftToggleButton = styled(IconButton)<{ collapsed: boolean }>(\n ({ collapsed }) => ({\n position: 'absolute',\n left: collapsed ? '10px' : '410px',\n top: '50%',\n transform: 'translateY(-50%)',\n zIndex: 1000,\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n border: '1px solid #ddd',\n transition: 'left 0.3s ease-in-out',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 1)',\n },\n })\n);\n\nexport const RightToggleButton = styled(IconButton)<{ collapsed: boolean }>(\n ({ collapsed }) => ({\n position: 'absolute',\n right: collapsed ? '10px' : '370px',\n top: '50%',\n transform: 'translateY(-50%)',\n zIndex: 1000,\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n border: '1px solid #ddd',\n transition: 'right 0.3s ease-in-out',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 1)',\n },\n })\n);\n\nexport const DisplayModeToggle = styled(IconButton)<{ mode: DisplayMode }>(({ mode }) => ({\n position: 'absolute',\n top: '50px',\n left: '75%',\n transform: 'translateX(-50%)',\n zIndex: 1000,\n backgroundColor:\n mode === 'dashboard' ? 'rgba(52, 152, 219, 0.9)' : 'rgba(255, 255, 255, 0.9)',\n color: mode === 'dashboard' ? 'white' : '#333',\n border: '1px solid #ddd',\n borderRadius: '20px',\n padding: '8px 16px',\n fontSize: '12px',\n fontWeight: '600',\n minWidth: '120px',\n transition: 'all 0.3s ease-in-out',\n '&:hover': {\n backgroundColor:\n mode === 'dashboard' ? 'rgba(52, 152, 219, 1)' : 'rgba(255, 255, 255, 1)',\n transform: 'translateX(-50%) scale(1.05)',\n },\n}));\n\nexport const ChevronLeft = () => (\n <div\n style={{\n width: 0,\n height: 0,\n borderTop: '6px solid transparent',\n borderBottom: '6px solid transparent',\n borderRight: '8px solid #666',\n }}\n />\n);\n\nexport const ChevronRight = () => (\n <div\n style={{\n width: 0,\n height: 0,\n borderTop: '6px solid transparent',\n borderBottom: '6px solid transparent',\n borderLeft: '8px solid #666',\n }}\n />\n);\n\nexport const ZoomControls = styled(Box)({\n position: 'absolute',\n top: '10px',\n right: '10px',\n display: 'flex',\n flexDirection: 'column',\n gap: '5px',\n zIndex: 1000,\n});\n\nexport const ZoomButton = styled(IconButton)({\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n border: '1px solid #ddd',\n width: '32px',\n height: '32px',\n fontSize: '14px',\n fontWeight: 'bold',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 1)',\n },\n});\n\nexport const FooterPanel = styled(Box)({\n gridArea: 'footer',\n background: 'rgba(255, 255, 255, 0.95)',\n border: '1px solid #ddd',\n borderRadius: '8px',\n padding: '10px',\n display: 'flex',\n justifyContent: 'space-around',\n alignItems: 'center',\n});\n\nexport const PanelTabs = styled(Tabs)({\n minHeight: '36px',\n '& .MuiTabs-indicator': {\n backgroundColor: '#546e7a',\n },\n '& .MuiTab-root': {\n minHeight: '36px',\n padding: '6px 12px',\n fontSize: '12px',\n fontWeight: '600',\n textTransform: 'none',\n color: '#666',\n '&.Mui-selected': {\n color: '#546e7a',\n },\n },\n});\n\nexport const PanelContent = styled(Box)({\n flex: 1,\n overflow: 'auto',\n padding: '10px',\n});\n\nexport const FixedSVGContainer = styled('div')({\n position: 'relative',\n width: '1300px',\n height: '400px',\n minWidth: '1300px',\n minHeight: '400px',\n maxWidth: '1300px',\n maxHeight: '400px',\n border: 'none',\n backgroundColor: 'transparent',\n overflow: 'visible',\n '& .svg-locked-overlay': {\n position: 'absolute',\n pointerEvents: 'auto',\n zIndex: 10,\n },\n '& svg': {\n position: 'absolute',\n left: 0,\n top: 0,\n width: '1300px',\n height: '280px',\n minWidth: '1300px',\n minHeight: '280px',\n maxWidth: 'none',\n maxHeight: 'none',\n objectFit: 'none',\n transform: 'none',\n border: 'none',\n },\n});\n\nexport const ScrollableSVGWrapper = styled('div')({\n position: 'relative',\n width: '100%',\n height: '100%',\n overflow: 'auto',\n transform: 'rotateX(180deg)',\n padding: '250px',\n paddingTop: '200px',\n boxSizing: 'border-box',\n '& > *': {\n display: 'block',\n transform: 'rotateX(180deg)',\n },\n});\n","import { useEffect, useState } from 'react';\nimport { Box } from '@mui/material';\nimport styled from '@emotion/styled';\nimport type { OverlayTheme, SVGLockedOverlayProps } from './types';\n\nconst baseOverlayStyles = {\n tank: {\n background:\n 'linear-gradient(135deg, rgba(69, 90, 120, 0.95) 0%, rgba(52, 73, 94, 0.95) 100%)',\n border: '1px solid #455a64',\n color: 'white',\n borderRadius: '4px',\n fontSize: '12px',\n fontWeight: '500',\n textAlign: 'center' as const,\n padding: '8px',\n minWidth: '60px',\n boxShadow: '0 1px 4px rgba(69, 90, 120, 0.2)',\n },\n pressure: {\n background:\n 'linear-gradient(135deg, rgba(30, 50, 90, 0.75) 0%, rgba(20, 35, 70, 0.75) 100%)',\n border: '1px solid #1e3a8a',\n color: 'white',\n borderRadius: '4px',\n fontSize: '11px',\n fontWeight: '500',\n textAlign: 'center' as const,\n padding: '6px',\n minWidth: '50px',\n boxShadow: '0 1px 4px rgba(30, 50, 90, 0.15)',\n },\n vessel: {\n background:\n 'linear-gradient(135deg, rgba(84, 110, 122, 0.95) 0%, rgba(69, 90, 100, 0.95) 100%)',\n border: '1px solid #546e7a',\n color: 'white',\n borderRadius: '4px',\n fontSize: '13px',\n fontWeight: '500',\n textAlign: 'center' as const,\n padding: '8px',\n minWidth: '70px',\n boxShadow: '0 1px 4px rgba(84, 110, 122, 0.2)',\n },\n flow: {\n background:\n 'linear-gradient(135deg, rgba(102, 125, 102, 0.95) 0%, rgba(85, 107, 85, 0.95) 100%)',\n border: '1px solid #689f38',\n color: 'white',\n borderRadius: '4px',\n fontSize: '11px',\n fontWeight: '500',\n textAlign: 'center' as const,\n padding: '5px',\n minWidth: '60px',\n boxShadow: '0 1px 4px rgba(102, 125, 102, 0.2)',\n },\n} as const;\n\nexport const overlayStyles: Record<OverlayTheme, typeof baseOverlayStyles[OverlayTheme]> =\n baseOverlayStyles;\n\nexport const SVGLockedOverlay = ({\n svgX,\n svgY,\n children,\n theme,\n style = {},\n className = '',\n onClick,\n containerSelector = '.tff-svg-container',\n}: SVGLockedOverlayProps) => {\n const [svgOffset, setSvgOffset] = useState({ x: 0, y: 0 });\n\n useEffect(() => {\n const updateSvgOffset = () => {\n const containerElement = document.querySelector(containerSelector);\n const svgElement = containerElement?.querySelector('svg');\n\n if (svgElement && containerElement) {\n const svgRect = svgElement.getBoundingClientRect();\n const containerRect = containerElement.getBoundingClientRect();\n\n setSvgOffset({\n x: svgRect.left - containerRect.left,\n y: svgRect.top - containerRect.top,\n });\n }\n };\n\n updateSvgOffset();\n window.addEventListener('resize', updateSvgOffset);\n return () => window.removeEventListener('resize', updateSvgOffset);\n }, [containerSelector]);\n\n const pixelX = svgX + svgOffset.x;\n const pixelY = svgY + svgOffset.y;\n\n const appliedStyle = theme ? { ...overlayStyles[theme], ...style } : style;\n\n return (\n <Box\n className={`svg-locked-overlay ${className}`.trim()}\n onClick={onClick}\n sx={{\n position: 'absolute',\n left: `${pixelX}px`,\n top: `${pixelY}px`,\n cursor: onClick ? 'pointer' : 'default',\n zIndex: 10,\n pointerEvents: 'auto',\n ...appliedStyle,\n }}\n >\n {children}\n </Box>\n );\n};\n\nexport const DashboardCard = styled(Box)<{ svgX: number; svgY: number; cardType?: string }>(\n ({ svgX, svgY, cardType }) => ({\n position: 'absolute',\n left: svgX,\n top: svgY,\n background:\n cardType === 'critical'\n ? 'linear-gradient(135deg, rgba(220, 53, 69, 0.95) 0%, rgba(173, 20, 87, 0.95) 100%)'\n : cardType === 'warning'\n ? 'linear-gradient(135deg, rgba(255, 193, 7, 0.95) 0%, rgba(255, 143, 0, 0.95) 100%)'\n : cardType === 'vessel'\n ? 'linear-gradient(135deg, rgba(46, 204, 113, 0.95) 0%, rgba(39, 174, 96, 0.95) 100%)'\n : cardType === 'process'\n ? 'linear-gradient(135deg, rgba(155, 89, 182, 0.95) 0%, rgba(142, 68, 173, 0.95) 100%)'\n : cardType === 'control'\n ? 'linear-gradient(135deg, rgba(26, 188, 156, 0.95) 0%, rgba(22, 160, 133, 0.95) 100%)'\n : 'linear-gradient(135deg, rgba(52, 152, 219, 0.95) 0%, rgba(41, 128, 185, 0.95) 100%)',\n border: '1px solid rgba(255, 255, 255, 0.2)',\n borderRadius: '12px',\n padding: '12px',\n minWidth: '140px',\n boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',\n backdropFilter: 'blur(10px)',\n color: 'white',\n fontSize: '12px',\n fontWeight: '600',\n textAlign: 'center',\n transform: 'translate(-50%, -50%)',\n transition: 'all 0.3s ease-in-out',\n cursor: 'pointer',\n '&:hover': {\n transform: 'translate(-50%, -50%) scale(1.05)',\n boxShadow: '0 12px 40px rgba(0, 0, 0, 0.4)',\n },\n })\n);\n\nexport const CardHeader = styled('div')<{ color?: string }>(({ color }) => ({\n fontSize: '10px',\n opacity: 0.9,\n marginBottom: '4px',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n color: color || 'inherit',\n borderBottom: color ? `1px solid ${color}40` : 'none',\n paddingBottom: color ? '2px' : '0',\n}));\n\nexport const CardValue = styled('div')({\n fontSize: '16px',\n fontWeight: '700',\n marginBottom: '2px',\n});\n\nexport const CardUnit = styled('div')({\n fontSize: '9px',\n opacity: 0.8,\n});\n\n","import type { CSSProperties } from 'react';\nimport type {\n CircularGaugeProps,\n EquipmentIndicatorProps,\n EquipmentIndicatorWithSparklineProps,\n SparklineProps,\n TrendLineProps,\n} from './types';\nimport { SVGLockedOverlay } from './FullscreenWorkspace.overlays';\nimport { FORCED_MARK_COLOR } from '../../../theme/statusColors';\n\nconst forcedTagStyle: CSSProperties = {\n position: 'absolute',\n top: '-6px',\n right: '-6px',\n width: '14px',\n height: '14px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '9px',\n fontWeight: 700,\n color: FORCED_MARK_COLOR.text,\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n borderRadius: '50%',\n pointerEvents: 'none',\n};\n\nexport const TrendLine = ({\n values,\n width = 60,\n height = 20,\n color = '#4fc3f7',\n strokeWidth = 1.5,\n backgroundColor = 'rgba(255, 255, 255, 0.1)',\n forced = false,\n}: TrendLineProps) => {\n if (!values || values.length < 2) {\n return (\n <div\n style={{\n position: 'relative',\n width,\n height,\n backgroundColor,\n borderRadius: '2px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <span style={{ fontSize: '8px', color: '#ccc' }}>No data</span>\n {forced && (\n <span style={forcedTagStyle} aria-label=\"Forced\">\n F\n </span>\n )}\n </div>\n );\n }\n\n const minValue = Math.min(...values);\n const maxValue = Math.max(...values);\n const range = maxValue - minValue;\n\n const normalizedValues =\n range === 0\n ? values.map(() => height / 2)\n : values.map((v) => height - 4 - ((v - minValue) / range) * (height - 8));\n\n const pathData = normalizedValues\n .map((y, i) => {\n const x = 4 + (i / (values.length - 1)) * (width - 8);\n return i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;\n })\n .join(' ');\n\n return (\n <div\n style={{\n width,\n height,\n backgroundColor,\n borderRadius: '2px',\n position: 'relative',\n overflow: 'visible',\n }}\n >\n <svg\n width={width}\n height={height}\n style={{ position: 'absolute', top: 0, left: 0, overflow: 'hidden', borderRadius: '2px' }}\n >\n <path\n d={pathData}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <circle\n cx={4 + (width - 8)}\n cy={normalizedValues[normalizedValues.length - 1]}\n r={1.5}\n fill={color}\n />\n </svg>\n {forced && (\n <span style={forcedTagStyle} aria-label=\"Forced\">\n F\n </span>\n )}\n </div>\n );\n};\n\nconst hexToRgba = (hex: string, alpha: number) => {\n const r = parseInt(hex.slice(1, 3), 16);\n const g = parseInt(hex.slice(3, 5), 16);\n const b = parseInt(hex.slice(5, 7), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n};\n\nexport const CircularGauge = ({\n value,\n max,\n unit,\n label,\n size = 60,\n color = 'rgba(255, 255, 255, 0.9)',\n forced = false,\n realValue,\n}: CircularGaugeProps) => {\n const percentage = Math.min((value / max) * 100, 100);\n const strokeWidth = 4;\n const radius = (size - strokeWidth) / 2;\n const circumference = radius * 2 * Math.PI;\n const strokeDasharray = circumference;\n const strokeDashoffset = circumference - (percentage / 100) * circumference;\n\n const isHex = color.startsWith('#');\n const innerCircleColor = isHex ? hexToRgba(color, 0.8) : color;\n const borderColor = isHex ? hexToRgba(color, 0.9) : color;\n const backgroundColor = isHex ? hexToRgba(color, 0.15) : color;\n const borderStrokeColor = isHex ? hexToRgba(color, 0.4) : color;\n\n return (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n }}\n >\n <div\n style={{\n position: 'relative',\n width: size,\n height: size,\n borderRadius: '50%',\n backgroundColor,\n border: forced ? `2px solid ${FORCED_MARK_COLOR.border}` : `1px solid ${borderStrokeColor}`,\n }}\n >\n <svg width={size} height={size} style={{ transform: 'rotate(-90deg)' }}>\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius - strokeWidth - 2}\n fill={innerCircleColor}\n stroke={borderColor}\n strokeWidth={1}\n />\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n stroke=\"rgba(255, 255, 255, 0.2)\"\n strokeWidth={strokeWidth}\n fill=\"transparent\"\n />\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n stroke={color}\n strokeWidth={strokeWidth}\n fill=\"transparent\"\n strokeDasharray={strokeDasharray}\n strokeDashoffset={strokeDashoffset}\n strokeLinecap=\"round\"\n style={{ transition: 'stroke-dashoffset 0.5s ease-in-out' }}\n />\n </svg>\n <div\n style={{\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n fontSize: '11px',\n fontWeight: '700',\n color: 'white',\n textAlign: 'center',\n textShadow: '0 1px 2px rgba(0, 0, 0, 0.5)',\n }}\n >\n {value.toFixed(1)}\n <div style={{ fontSize: '8px', opacity: 0.9, color: 'white' }}>{unit}</div>\n </div>\n </div>\n <div\n style={{\n fontSize: '9px',\n marginTop: '4px',\n opacity: 0.9,\n color: 'white',\n }}\n >\n {label}\n </div>\n {forced && (\n <span\n aria-label=\"Forced\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '14px',\n height: '14px',\n fontSize: '9px',\n fontWeight: 700,\n color: FORCED_MARK_COLOR.text,\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n borderRadius: '50%',\n marginTop: '2px',\n }}\n >\n F\n </span>\n )}\n {forced && realValue !== undefined && (\n <div style={{ fontSize: '8px', opacity: 0.8, color: 'white', marginTop: '1px' }}>\n REAL {realValue.toFixed(1)} {unit}\n </div>\n )}\n </div>\n );\n};\n\nexport const Sparkline = ({ data, width, height, color, forced = false }: SparklineProps) => {\n if (!data || data.length < 2) {\n return null;\n }\n\n const max = Math.max(...data);\n const min = Math.min(...data);\n const range = max - min;\n\n if (range === 0) {\n return null;\n }\n\n const points = data\n .map((value, index) => {\n const x = (index / (data.length - 1)) * (width - 8);\n const y = height - 8 - ((value - min) / range) * (height - 8);\n return `${x + 4},${y + 4}`;\n })\n .join(' ');\n\n return (\n <div\n style={{\n position: 'relative',\n width,\n height,\n backgroundColor: 'rgba(0, 0, 0, 0.7)',\n border: forced ? `1px solid ${FORCED_MARK_COLOR.border}` : '1px solid rgba(255, 255, 255, 0.3)',\n borderRadius: '4px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n boxShadow: '0 1px 3px rgba(0, 0, 0, 0.2)',\n }}\n >\n <svg width={width} height={height}>\n <polyline points={points} fill=\"none\" stroke={color} strokeWidth=\"1.5\" opacity=\"0.9\" />\n </svg>\n {forced && (\n <span style={forcedTagStyle} aria-label=\"Forced\">\n F\n </span>\n )}\n </div>\n );\n};\n\n/** Small glyph badge for EquipmentIndicator variants (#3607, R19). A glyph,\n * not a color change: this indicator's whole signal is color plus a short\n * label today, and color alone fails a colorblind operator. */\nconst EquipmentForcedBadge = () => (\n <span\n aria-label=\"Forced\"\n style={{\n position: 'absolute',\n top: '-4px',\n right: '-4px',\n width: '11px',\n height: '11px',\n borderRadius: '50%',\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n color: FORCED_MARK_COLOR.text,\n fontSize: '7px',\n fontWeight: 700,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n lineHeight: 1,\n }}\n >\n F\n </span>\n);\n\nexport const EquipmentIndicatorWithSparkline = ({\n svgX,\n svgY,\n color,\n label,\n size = 30,\n sparklineData,\n sparklineOffset = { x: 40, y: 0 },\n forced = false,\n}: EquipmentIndicatorWithSparklineProps) => {\n const hasSparkline =\n sparklineData &&\n sparklineData.length > 1 &&\n Math.max(...sparklineData) !== Math.min(...sparklineData);\n\n return (\n <>\n <SVGLockedOverlay svgX={svgX} svgY={svgY} style={{ transform: 'translate(-50%, -50%)' }}>\n <div style={{ position: 'relative', width: size, height: size }}>\n <div\n style={{\n width: size,\n height: size,\n borderRadius: '50%',\n backgroundColor: color,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: 'white',\n fontSize: '7px',\n fontWeight: '700',\n textAlign: 'center',\n boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',\n border: forced\n ? `2px solid ${FORCED_MARK_COLOR.border}`\n : '2px solid rgba(255, 255, 255, 0.3)',\n }}\n >\n {label}\n </div>\n {forced && <EquipmentForcedBadge />}\n </div>\n </SVGLockedOverlay>\n\n {hasSparkline && (\n <SVGLockedOverlay svgX={svgX + sparklineOffset.x} svgY={svgY + sparklineOffset.y}>\n <Sparkline data={sparklineData!} width={60} height={20} color={color} forced={forced} />\n </SVGLockedOverlay>\n )}\n </>\n );\n};\n\nexport const EquipmentIndicator = ({\n svgX,\n svgY,\n color,\n label,\n size = 30,\n style,\n className,\n forced = false,\n}: EquipmentIndicatorProps) => {\n const overlayStyle =\n style !== undefined\n ? { transform: 'translate(-50%, -50%)', ...style }\n : { transform: 'translate(-50%, -50%)' };\n\n return (\n <SVGLockedOverlay svgX={svgX} svgY={svgY} style={overlayStyle} className={className}>\n <div style={{ position: 'relative', width: size, height: size }}>\n <div\n style={{\n width: size,\n height: size,\n borderRadius: '50%',\n backgroundColor: color,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: 'white',\n fontSize: '7px',\n fontWeight: '700',\n textAlign: 'center',\n boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',\n border: forced\n ? `2px solid ${FORCED_MARK_COLOR.border}`\n : '2px solid rgba(255, 255, 255, 0.3)',\n }}\n >\n {label}\n </div>\n {forced && <EquipmentForcedBadge />}\n </div>\n </SVGLockedOverlay>\n );\n};\n","import React, { createContext, useContext, useState, useEffect } from 'react';\n\ntype Theme = 'modern' | 'hmi';\n\ninterface ThemeContextType {\n theme: Theme;\n toggleTheme: () => void;\n setTheme: (theme: Theme) => void;\n}\n\nconst ThemeContext = createContext<ThemeContextType | undefined>(undefined);\n\nexport const useTheme = () => {\n const context = useContext(ThemeContext);\n if (!context) {\n throw new Error('useTheme must be used within a ThemeProvider');\n }\n return context;\n};\n\ninterface ThemeProviderProps {\n children: React.ReactNode;\n}\n\nexport const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {\n const [theme, setThemeState] = useState<Theme>('modern');\n\n useEffect(() => {\n // Apply theme classes to document body\n document.body.className = '';\n document.body.classList.add(`theme-${theme}`);\n\n // Also apply to html element for global styling\n document.documentElement.className = '';\n document.documentElement.classList.add(`theme-${theme}`);\n }, [theme]);\n\n const toggleTheme = () => {\n setTheme(theme === 'modern' ? 'hmi' : 'modern');\n };\n\n const setTheme = (newTheme: Theme) => {\n setThemeState(newTheme);\n };\n\n return (\n <ThemeContext.Provider value={{ theme, toggleTheme, setTheme }}>\n {children}\n </ThemeContext.Provider>\n );\n};\n","import React from 'react';\nimport { useTheme } from './ThemeContext';\n\nexport interface ThemeToggleProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Size of the toggle */\n size?: 'small' | 'medium' | 'large';\n /** Position of the toggle */\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'inline';\n}\n\nexport const ThemeToggle: React.FC<ThemeToggleProps> = ({\n size = 'medium',\n position = 'top-right',\n className = '',\n ...props\n}) => {\n const { theme, toggleTheme } = useTheme();\n\n const sizeStyles = {\n small: { padding: '0.5rem 1rem', fontSize: '0.75rem' },\n medium: { padding: '0.75rem 1.5rem', fontSize: '0.875rem' },\n large: { padding: '1rem 2rem', fontSize: '1rem' },\n };\n\n const positionStyles = {\n 'top-left': { position: 'fixed' as const, top: '1rem', left: '1rem', zIndex: 1000 },\n 'top-right': { position: 'fixed' as const, top: '1rem', right: '1rem', zIndex: 1000 },\n 'bottom-left': { position: 'fixed' as const, bottom: '1rem', left: '1rem', zIndex: 1000 },\n 'bottom-right': { position: 'fixed' as const, bottom: '1rem', right: '1rem', zIndex: 1000 },\n inline: { position: 'relative' as const },\n };\n\n const baseStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '0.75rem',\n cursor: 'pointer',\n borderRadius: '0.5rem',\n border: '2px solid',\n fontFamily:\n 'ui-monospace, SFMono-Regular, \"SF Mono\", Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n transition: 'all 0.2s ease',\n userSelect: 'none',\n ...sizeStyles[size],\n ...positionStyles[position],\n };\n\n const modernStyle: React.CSSProperties = {\n ...baseStyle,\n background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 50%, #1d4ed8 100%)',\n borderColor: 'rgba(59, 130, 246, 0.4)',\n color: '#ffffff',\n boxShadow: '0 8px 32px rgba(59, 130, 246, 0.3)',\n };\n\n const hmiStyle: React.CSSProperties = {\n ...baseStyle,\n backgroundColor: '#666666',\n borderColor: '#999999',\n color: '#ffffff',\n boxShadow: 'none',\n };\n\n const currentStyle = theme === 'modern' ? modernStyle : hmiStyle;\n\n const toggleStyle: React.CSSProperties = {\n position: 'relative',\n width: '3rem',\n height: '1.5rem',\n borderRadius: '0.75rem',\n transition: 'all 0.2s ease',\n backgroundColor: theme === 'modern' ? '#3b82f6' : '#808080',\n border: `2px solid ${theme === 'modern' ? '#2563eb' : '#666666'}`,\n };\n\n const thumbStyle: React.CSSProperties = {\n position: 'absolute',\n top: '0.125rem',\n left: theme === 'modern' ? '0.125rem' : '1.375rem',\n width: '1rem',\n height: '1rem',\n borderRadius: '50%',\n backgroundColor: '#ffffff',\n transition: 'all 0.2s ease',\n boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)',\n };\n\n return (\n <div style={currentStyle} onClick={toggleTheme} className={className} {...props}>\n <span>{theme === 'modern' ? '🎨 Modern' : '🏭 HMI'}</span>\n <div style={toggleStyle}>\n <div style={thumbStyle} />\n </div>\n <span style={{ fontSize: '0.75rem', opacity: 0.8 }}>\n {theme === 'modern' ? 'Industrial Design' : 'High Performance'}\n </span>\n </div>\n );\n};\n","import React, { useCallback, useState, useEffect, useRef } from 'react';\nimport type { Diagram } from '../../diagram/schema';\nimport type { TelemetryMap } from '../../diagram/schema';\nimport { NodeRenderer } from './NodeRenderer';\nimport { PipeRenderer } from './PipeRenderer';\nimport { DataOverlay } from './DataOverlay';\nimport { PositionPanel } from './PositionPanel';\nimport { useViewBox, useSelection, useDrag, useKeyboardShortcuts } from '../../diagram/hooks';\nimport type { SelectionState } from '../../diagram/hooks';\nimport { getSymbolDefinition } from '../../diagram/symbols';\nimport { generateRoutePoints, getPortWorldPosition } from '../../diagram/utils/portCalculations';\nimport type { StatusColorMap } from '../../theme/statusColors';\n\nexport interface PIDCanvasFeatures {\n /** Enable pan interaction (default: true) */\n pan?: boolean;\n /** Enable zoom interaction (default: true) */\n zoom?: boolean;\n /** Enable selection interaction (default: false) */\n selection?: boolean;\n /** Allow multi-selection with Ctrl/Cmd key (default: true) */\n multiSelect?: boolean;\n /** Enable telemetry data overlays (default: false) */\n telemetry?: boolean;\n /** Scale factor for telemetry displays (default: 1.0, range: 0.5-2.0) */\n telemetryScale?: number;\n /** Enable drag-and-drop for nodes (default: false) */\n dragNodes?: boolean;\n /** Snap dragged nodes to grid (0 = no snap) */\n snapToGrid?: number;\n /** Enable keyboard shortcuts for delete/copy/paste (default: false) */\n keyboardShortcuts?: boolean;\n /** Enable pipe waypoint editing (drag waypoints, add/remove) (default: false) */\n editPipeRoutes?: boolean;\n /** Enable dropping symbols from library to add nodes (default: false) */\n allowSymbolDrop?: boolean;\n /** Enable connection drawing mode to create pipes between nodes (default: false) */\n connectionMode?: boolean;\n /** Show background grid (default: true) */\n showGrid?: boolean;\n /** Background color for canvas (default: '#ffffff') */\n backgroundColor?: string;\n}\n\nexport interface PIDCanvasProps extends React.HTMLAttributes<HTMLDivElement> {\n /** The diagram to render */\n diagram: Diagram;\n /** Optional className for the container */\n className?: string;\n /** Optional inline styles for the container */\n style?: React.CSSProperties;\n /** Enable interactive features (pan, zoom, selection, telemetry, dragNodes) */\n features?: PIDCanvasFeatures;\n /** Callback when selection changes */\n onSelectionChange?: (selection: SelectionState) => void;\n /** Callback when a node is clicked */\n onNodeClick?: (nodeId: string) => void;\n /** Callback when a pipe is clicked */\n onPipeClick?: (pipeId: string) => void;\n /** Callback when a node is moved (drag-and-drop) */\n onNodeMove?: (nodeId: string, newX: number, newY: number) => void;\n /** Callback when diagram changes (for internal state updates) */\n onDiagramChange?: (diagram: Diagram) => void;\n /** Callback when items are deleted */\n onDelete?: (nodeIds: string[], pipeIds: string[]) => void;\n /** Callback when items are copied (returns copied data for external storage) */\n onItemsCopied?: (nodeIds: string[], pipeIds: string[]) => void;\n /** Callback when paste is requested (external code should handle the paste logic) */\n onPaste?: () => void;\n /** Callback when a symbol is dropped onto canvas (returns symbol ID and position) */\n onSymbolDrop?: (symbolId: string, position: { x: number; y: number }) => void;\n /** Callback when a pipe is created via connection mode */\n onPipeCreated?: (\n sourceNodeId: string,\n targetNodeId: string,\n sourcePortId?: string,\n targetPortId?: string\n ) => void;\n /** Callback when canvas is mounted, provides control functions */\n onMounted?: (controls: PIDCanvasControls) => void;\n /** Telemetry data bindings for live values */\n telemetry?: TelemetryMap;\n /** Custom status-to-color map for node rendering (overrides ISA-101 defaults) */\n statusColorMap?: StatusColorMap;\n}\n\nexport interface PIDCanvasControls {\n /** Fit view to show all diagram content */\n fitToContent: (padding?: number) => void;\n /** Set zoom level (1 = 100%, 2 = 200%, etc.) */\n setZoom: (zoomLevel: number) => void;\n /** Get current zoom level */\n getZoom: () => number;\n}\n\n/**\n * Default feature set. Hoisted to module scope so an omitted `features` prop\n * does not mint a fresh object per render - that would defeat the React.memo\n * boundary on PIDCanvas below (renderer-OOM fix).\n */\nconst DEFAULT_FEATURES: PIDCanvasFeatures = {\n pan: true,\n zoom: true,\n selection: false,\n multiSelect: true,\n telemetry: false,\n dragNodes: false,\n};\n\n/**\n * PIDCanvas - Interactive canvas for rendering P&ID diagrams\n *\n * Features:\n * - Renders nodes (PR #3) and pipes (PR #4) with static layout\n * - Drag-and-drop: Drag nodes to reposition (PR #8)\n *\n * Memoized (renderer-OOM fix): a host whose screen re-renders for reasons\n * unrelated to the canvas (panel state, connection status, ...) should not pay\n * a full-canvas reconciliation. The memo only pays off when the host passes\n * identity-stable props (memoized features/telemetry/style, useCallback\n * handlers); an unstable host degrades gracefully to the old behavior.\n *\n * Future: Editing tools, symbol library UI\n */\nfunction PIDCanvasImpl({\n diagram,\n className = '',\n style,\n features = DEFAULT_FEATURES,\n onSelectionChange,\n onNodeClick,\n onPipeClick,\n onNodeMove,\n onDiagramChange,\n onDelete,\n onItemsCopied,\n onPaste,\n onSymbolDrop,\n onPipeCreated,\n onMounted,\n telemetry,\n statusColorMap,\n ...props\n}: PIDCanvasProps): React.ReactElement {\n const [localDiagram, setLocalDiagram] = useState(diagram);\n const [dragOverPosition, setDragOverPosition] = useState<{ x: number; y: number } | null>(null);\n\n // Connection mode state\n const [isConnecting, setIsConnecting] = useState(false);\n const [connectionSource, setConnectionSource] = useState<string | null>(null);\n const [connectionSourcePort, setConnectionSourcePort] = useState<string | null>(null);\n const [connectionCursor, setConnectionCursor] = useState<{ x: number; y: number } | null>(null);\n const [hoveredNode, setHoveredNode] = useState<string | null>(null);\n const [hoveredPort, setHoveredPort] = useState<string | null>(null);\n\n // Waypoint selection state (separate from node/pipe selection)\n const [selectedWaypoint, setSelectedWaypoint] = useState<{\n pipeId: string;\n pointIndex: number;\n } | null>(null);\n\n // Undo/Redo state - using refs to avoid re-renders\n const historyStack = useRef<Diagram[]>([]);\n const redoStack = useRef<Diagram[]>([]);\n const isUndoRedoAction = useRef(false);\n const isInternalChange = useRef(false);\n const lastInternalDiagram = useRef<Diagram | null>(null);\n\n // Sync external diagram changes to local state\n useEffect(() => {\n // Only reset history if this is a truly external change (not from our callbacks)\n const isExternal = !isInternalChange.current && diagram !== lastInternalDiagram.current;\n\n // Only update state if this is an external change\n // This is a legitimate use of setState in effect - syncing external prop to local state\n if (isExternal) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setLocalDiagram(diagram);\n\n // Reset history for external changes\n if (!isUndoRedoAction.current) {\n historyStack.current = [];\n redoStack.current = [];\n }\n }\n\n // Reset the internal change flag\n isInternalChange.current = false;\n }, [diagram]);\n\n const {\n viewBox: controlledViewBox,\n svgRef,\n screenToWorld,\n fitToContent: fitToContentViewBox,\n setViewBox,\n } = useViewBox({\n initialViewBox: diagram.viewBox,\n enablePan: features.pan ?? true,\n enableZoom: features.zoom ?? true,\n });\n\n // Calculate content bounds\n const calculateContentBounds = useCallback(() => {\n const nodes = localDiagram.nodes.filter((n) => n.visible !== false);\n if (nodes.length === 0) {\n return null;\n }\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n nodes.forEach((node) => {\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol) return;\n\n const nodeMinX = node.transform.x;\n const nodeMinY = node.transform.y;\n const nodeMaxX = node.transform.x + symbol.viewBox.width;\n const nodeMaxY = node.transform.y + symbol.viewBox.height;\n\n minX = Math.min(minX, nodeMinX);\n minY = Math.min(minY, nodeMinY);\n maxX = Math.max(maxX, nodeMaxX);\n maxY = Math.max(maxY, nodeMaxY);\n });\n\n return { minX, minY, maxX, maxY };\n }, [localDiagram.nodes]);\n\n // Expose controls via onMounted callback\n useEffect(() => {\n if (onMounted) {\n const controls: PIDCanvasControls = {\n fitToContent: (padding = 50) => {\n const bounds = calculateContentBounds();\n if (bounds) {\n fitToContentViewBox(bounds, padding);\n }\n },\n setZoom: (zoomLevel: number) => {\n const centerX = controlledViewBox.x + controlledViewBox.width / 2;\n const centerY = controlledViewBox.y + controlledViewBox.height / 2;\n const newWidth = diagram.viewBox.width / zoomLevel;\n const newHeight = diagram.viewBox.height / zoomLevel;\n setViewBox({\n x: centerX - newWidth / 2,\n y: centerY - newHeight / 2,\n width: newWidth,\n height: newHeight,\n });\n },\n getZoom: () => {\n return diagram.viewBox.width / controlledViewBox.width;\n },\n };\n onMounted(controls);\n }\n }, [\n onMounted,\n calculateContentBounds,\n fitToContentViewBox,\n setViewBox,\n controlledViewBox,\n diagram.viewBox,\n ]);\n\n const { selection, selectNode, selectPipe, clearSelection } = useSelection({\n multiSelect: features.multiSelect ?? true,\n onSelectionChange,\n });\n\n const dragEnabled = features.dragNodes ?? false;\n const selectionEnabled = features.selection ?? false;\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Push current state to history (for undo)\n const pushToHistory = useCallback((currentDiagram: Diagram) => {\n if (isUndoRedoAction.current) return; // Don't add to history during undo/redo\n\n // Deep clone to avoid reference issues\n const snapshot = JSON.parse(JSON.stringify(currentDiagram));\n historyStack.current.push(snapshot);\n\n // Limit history size to 15 states\n if (historyStack.current.length > 15) {\n historyStack.current.shift();\n }\n\n // Clear redo stack on new action\n redoStack.current = [];\n }, []);\n\n // Undo function\n const undo = useCallback(() => {\n if (historyStack.current.length === 0) return;\n\n isUndoRedoAction.current = true;\n\n // Save current state to redo stack\n const currentSnapshot = JSON.parse(JSON.stringify(localDiagram));\n redoStack.current.push(currentSnapshot);\n\n // Pop from history and restore\n const previousState = historyStack.current.pop()!;\n isInternalChange.current = true;\n lastInternalDiagram.current = previousState;\n setLocalDiagram(previousState);\n onDiagramChange?.(previousState);\n\n // Reset flag after state updates\n setTimeout(() => {\n isUndoRedoAction.current = false;\n }, 0);\n }, [localDiagram, onDiagramChange]);\n\n // Redo function\n const redo = useCallback(() => {\n if (redoStack.current.length === 0) return;\n\n isUndoRedoAction.current = true;\n\n // Save current state to history\n const currentSnapshot = JSON.parse(JSON.stringify(localDiagram));\n historyStack.current.push(currentSnapshot);\n\n // Pop from redo stack and restore\n const nextState = redoStack.current.pop()!;\n isInternalChange.current = true;\n lastInternalDiagram.current = nextState;\n setLocalDiagram(nextState);\n onDiagramChange?.(nextState);\n\n // Reset flag after state updates\n setTimeout(() => {\n isUndoRedoAction.current = false;\n }, 0);\n }, [localDiagram, onDiagramChange]);\n\n // Keyboard shortcuts for undo/redo\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // Check for Ctrl+Z (Windows/Linux) or Cmd+Z (Mac)\n const isCtrlOrCmd = e.ctrlKey || e.metaKey;\n\n if (isCtrlOrCmd && e.key === 'z' && !e.shiftKey) {\n e.preventDefault();\n undo();\n } else if (isCtrlOrCmd && e.key === 'z' && e.shiftKey) {\n e.preventDefault();\n redo();\n } else if (isCtrlOrCmd && e.key === 'y') {\n // Ctrl+Y is also a common redo shortcut\n e.preventDefault();\n redo();\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [undo, redo]);\n\n // Keyboard nudging for selected nodes and waypoints\n useEffect(() => {\n if (!features.dragNodes) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n // Only handle arrow keys when something is selected\n if (selection.selectedNodeIds.size === 0 && !selectedWaypoint) return;\n\n // Ignore if user is typing in an input\n if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {\n return;\n }\n\n const nudgeAmount = e.shiftKey ? 10 : 1; // Shift for larger nudge\n let dx = 0;\n let dy = 0;\n\n switch (e.key) {\n case 'ArrowLeft':\n dx = -nudgeAmount;\n e.preventDefault();\n break;\n case 'ArrowRight':\n dx = nudgeAmount;\n e.preventDefault();\n break;\n case 'ArrowUp':\n dy = -nudgeAmount;\n e.preventDefault();\n break;\n case 'ArrowDown':\n dy = nudgeAmount;\n e.preventDefault();\n break;\n default:\n return;\n }\n\n // Update selected waypoint if one is selected\n if (selectedWaypoint) {\n pushToHistory(localDiagram);\n\n const updatedPipes = localDiagram.pipes.map((pipe) => {\n if (pipe.id === selectedWaypoint.pipeId) {\n const updatedRoutePoints = pipe.routePoints.map((point, idx) =>\n idx === selectedWaypoint.pointIndex ? { x: point.x + dx, y: point.y + dy } : point\n );\n return { ...pipe, routePoints: updatedRoutePoints };\n }\n return pipe;\n });\n\n const updatedDiagram = { ...localDiagram, pipes: updatedPipes };\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n return;\n }\n\n // Update all selected nodes\n if (selectionEnabled && selection.selectedNodeIds.size > 0) {\n pushToHistory(localDiagram);\n\n const updatedNodes = localDiagram.nodes.map((node) =>\n selection.selectedNodeIds.has(node.id)\n ? {\n ...node,\n transform: {\n ...node.transform,\n x: node.transform.x + dx,\n y: node.transform.y + dy,\n },\n }\n : node\n );\n\n const updatedDiagram = { ...localDiagram, nodes: updatedNodes };\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n\n // Call onNodeMove for each moved node\n selection.selectedNodeIds.forEach((nodeId) => {\n const node = updatedNodes.find((n) => n.id === nodeId);\n if (node) {\n onNodeMove?.(nodeId, node.transform.x, node.transform.y);\n }\n });\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [\n selectionEnabled,\n features.dragNodes,\n selection.selectedNodeIds,\n selectedWaypoint,\n localDiagram,\n onDiagramChange,\n onNodeMove,\n pushToHistory,\n ]);\n\n // Handler for node position panel changes\n const handlePositionChange = useCallback(\n (nodeId: string, x: number, y: number) => {\n pushToHistory(localDiagram);\n\n const updatedNodes = localDiagram.nodes.map((node) =>\n node.id === nodeId\n ? {\n ...node,\n transform: {\n ...node.transform,\n x,\n y,\n },\n }\n : node\n );\n\n const updatedDiagram = { ...localDiagram, nodes: updatedNodes };\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n onNodeMove?.(nodeId, x, y);\n },\n [localDiagram, onDiagramChange, onNodeMove, pushToHistory]\n );\n\n // Handler for waypoint position panel changes\n const handleWaypointPositionChange = useCallback(\n (pipeId: string, pointIndex: number, x: number, y: number) => {\n pushToHistory(localDiagram);\n\n const updatedPipes = localDiagram.pipes.map((pipe) => {\n if (pipe.id === pipeId) {\n const updatedRoutePoints = pipe.routePoints.map((point, idx) =>\n idx === pointIndex ? { x, y } : point\n );\n return { ...pipe, routePoints: updatedRoutePoints };\n }\n return pipe;\n });\n\n const updatedDiagram = { ...localDiagram, pipes: updatedPipes };\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n },\n [localDiagram, onDiagramChange, pushToHistory]\n );\n\n const handleDragEnd = useCallback(\n (nodeId: string, offset: { x: number; y: number }, finalPosition: { x: number; y: number }) => {\n // If drag offset is zero or very small, treat as a click for selection\n const dragDistance = Math.sqrt(offset.x ** 2 + offset.y ** 2);\n if (dragDistance < 2) {\n // This was a click, not a drag - handle selection\n if (selectionEnabled) {\n // We need to check if this was a Ctrl/Cmd click, but we don't have the event here\n // For now, just select without modifier\n selectNode(nodeId, false);\n }\n return;\n }\n\n pushToHistory(localDiagram);\n\n // Update node position in diagram\n const updatedNodes = localDiagram.nodes.map((node) =>\n node.id === nodeId\n ? {\n ...node,\n transform: {\n ...node.transform,\n x: finalPosition.x,\n y: finalPosition.y,\n },\n }\n : node\n );\n\n // Find affected pipes (pipes connected to this node)\n const affectedPipes = localDiagram.pipes.filter(\n (pipe) => pipe.fromNodeId === nodeId || pipe.toNodeId === nodeId\n );\n\n // Recalculate routes for affected pipes\n const updatedPipes = localDiagram.pipes.map((pipe) => {\n if (!affectedPipes.find((p) => p.id === pipe.id)) return pipe;\n\n const sourceNode = updatedNodes.find((n) => n.id === pipe.fromNodeId);\n const targetNode = updatedNodes.find((n) => n.id === pipe.toNodeId);\n\n if (!sourceNode || !targetNode) return pipe;\n\n // If pipe has custom waypoints (more than 2 points), preserve ALL waypoints\n // including endpoints - user has manually positioned them\n if (pipe.routePoints.length > 2) {\n return pipe; // Keep all waypoints as-is, including endpoints\n }\n\n // For simple pipes (2 points), recalculate to follow node ports\n const newRoutePoints = generateRoutePoints(\n sourceNode,\n pipe.fromPortId ?? 'output',\n targetNode,\n pipe.toPortId ?? 'input'\n );\n\n return {\n ...pipe,\n routePoints: newRoutePoints,\n };\n });\n\n const updatedDiagram: Diagram = {\n ...localDiagram,\n nodes: updatedNodes,\n pipes: updatedPipes,\n };\n\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n onNodeMove?.(nodeId, finalPosition.x, finalPosition.y);\n },\n [localDiagram, onDiagramChange, onNodeMove, selectionEnabled, selectNode, pushToHistory]\n );\n\n const { dragState, startDrag } = useDrag({\n snapToGrid: features.snapToGrid ?? 0,\n screenToWorld,\n onDragEnd: handleDragEnd,\n });\n\n // Separate drag state for waypoints\n const { dragState: waypointDragState, startDrag: startWaypointDrag } = useDrag({\n snapToGrid: features.snapToGrid ?? 0,\n screenToWorld,\n onDragEnd: (\n pipeId: string,\n _offset: { x: number; y: number },\n finalPosition: { x: number; y: number }\n ) => {\n pushToHistory(localDiagram);\n\n // Update waypoint position\n const [actualPipeId, pointIndex] = pipeId.split(':');\n const idx = parseInt(pointIndex, 10);\n\n const updatedPipes = localDiagram.pipes.map((pipe) => {\n if (pipe.id !== actualPipeId) return pipe;\n\n const updatedRoutePoints = pipe.routePoints.map((point, i) =>\n i === idx ? { x: finalPosition.x, y: finalPosition.y } : point\n );\n\n return { ...pipe, routePoints: updatedRoutePoints };\n });\n\n const updatedDiagram = { ...localDiagram, pipes: updatedPipes };\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n },\n });\n\n const handleNodeDragStart = useCallback(\n (nodeId: string, event: React.MouseEvent | React.TouchEvent) => {\n const node = localDiagram.nodes.find((n) => n.id === nodeId);\n if (!node) return;\n startDrag(nodeId, event, { x: node.transform.x, y: node.transform.y });\n },\n [localDiagram.nodes, startDrag]\n );\n\n const handleWaypointClick = useCallback(\n (pipeId: string, pointIndex: number) => {\n // Select this waypoint\n setSelectedWaypoint({ pipeId, pointIndex });\n\n // Clear node/pipe selection\n if (selectionEnabled) {\n clearSelection();\n }\n },\n [selectionEnabled, clearSelection]\n );\n\n const handleWaypointDragStart = useCallback(\n (pipeId: string, pointIndex: number, event: React.MouseEvent | React.TouchEvent) => {\n const pipe = localDiagram.pipes.find((p) => p.id === pipeId);\n if (!pipe || pointIndex >= pipe.routePoints.length) return;\n\n const point = pipe.routePoints[pointIndex];\n\n // Use compound ID to track both pipe and point\n startWaypointDrag(`${pipeId}:${pointIndex}`, event, { x: point.x, y: point.y });\n },\n [localDiagram.pipes, startWaypointDrag]\n );\n\n const handlePipeSegmentClick = useCallback(\n (pipeId: string, position: { x: number; y: number }, segmentIndex: number) => {\n pushToHistory(localDiagram);\n\n const updatedPipes = localDiagram.pipes.map((pipe) => {\n if (pipe.id !== pipeId) return pipe;\n\n // Insert new waypoint after segmentIndex\n const updatedRoutePoints = [\n ...pipe.routePoints.slice(0, segmentIndex + 1),\n position,\n ...pipe.routePoints.slice(segmentIndex + 1),\n ];\n\n return { ...pipe, routePoints: updatedRoutePoints };\n });\n\n const updatedDiagram = { ...localDiagram, pipes: updatedPipes };\n setLocalDiagram(updatedDiagram);\n onDiagramChange?.(updatedDiagram);\n },\n [localDiagram, onDiagramChange, pushToHistory]\n );\n\n const telemetryEnabled = features.telemetry ?? false;\n\n const handleDelete = useCallback(() => {\n if (!selectionEnabled) return;\n const { selectedNodeIds, selectedPipeIds } = selection;\n\n if (selectedNodeIds.size === 0 && selectedPipeIds.size === 0) return;\n\n pushToHistory(localDiagram);\n\n // Remove selected nodes\n const updatedNodes = localDiagram.nodes.filter((node) => !selectedNodeIds.has(node.id));\n\n // Remove selected pipes and pipes connected to deleted nodes\n const updatedPipes = localDiagram.pipes.filter(\n (pipe) =>\n !selectedPipeIds.has(pipe.id) &&\n !selectedNodeIds.has(pipe.fromNodeId) &&\n !selectedNodeIds.has(pipe.toNodeId)\n );\n\n const updatedDiagram = {\n ...localDiagram,\n nodes: updatedNodes,\n pipes: updatedPipes,\n };\n\n isInternalChange.current = true;\n lastInternalDiagram.current = updatedDiagram;\n setLocalDiagram(updatedDiagram);\n clearSelection();\n onDiagramChange?.(updatedDiagram);\n onDelete?.(Array.from(selectedNodeIds), Array.from(selectedPipeIds));\n }, [\n selection,\n localDiagram,\n selectionEnabled,\n clearSelection,\n onDiagramChange,\n onDelete,\n pushToHistory,\n ]);\n\n const handleCopy = useCallback(() => {\n if (!selectionEnabled) return;\n const { selectedNodeIds, selectedPipeIds } = selection;\n\n if (selectedNodeIds.size === 0 && selectedPipeIds.size === 0) return;\n\n onItemsCopied?.(Array.from(selectedNodeIds), Array.from(selectedPipeIds));\n }, [selection, selectionEnabled, onItemsCopied]);\n\n const handlePaste = useCallback(() => {\n if (!selectionEnabled) return;\n onPaste?.();\n }, [selectionEnabled, onPaste]);\n\n // useCallback (here and on the node/pipe click handlers below) keeps the\n // identity handed to the memoized NodeRenderer/PipeRenderer stable across\n // telemetry-driven re-renders, so those layers can bail out (renderer-OOM\n // fix). Their deps change on interaction (selection, connection mode), never\n // on a telemetry tick.\n const handleNodeDoubleClick = useCallback(\n (nodeId: string, event: React.MouseEvent) => {\n if (!selectionEnabled) return;\n console.log('Node double-clicked:', nodeId);\n selectNode(nodeId, event.ctrlKey || event.metaKey);\n },\n [selectionEnabled, selectNode]\n );\n\n useKeyboardShortcuts({\n enabled: features.keyboardShortcuts ?? false,\n onDelete: handleDelete,\n onCopy: handleCopy,\n onPaste: handlePaste,\n });\n\n const handleNodeClick = useCallback(\n (nodeId: string, event: React.MouseEvent) => {\n // Connection mode takes priority\n const connectionModeEnabled = features.connectionMode ?? false;\n\n if (connectionModeEnabled && isConnecting) {\n // Second click - complete connection\n if (connectionSource && connectionSource !== nodeId) {\n // Use hovered port if available, otherwise let handler choose\n onPipeCreated?.(\n connectionSource,\n nodeId,\n connectionSourcePort || undefined,\n hoveredPort || undefined\n );\n setIsConnecting(false);\n setConnectionSource(null);\n setConnectionSourcePort(null);\n setConnectionCursor(null);\n setHoveredPort(null);\n }\n return;\n }\n\n if (connectionModeEnabled && !isConnecting) {\n // First click - start connection\n setIsConnecting(true);\n setConnectionSource(nodeId);\n setConnectionSourcePort(hoveredPort);\n return;\n }\n\n // Normal selection mode\n if (selectionEnabled) {\n selectNode(nodeId, event.ctrlKey || event.metaKey);\n }\n onNodeClick?.(nodeId);\n },\n [\n features.connectionMode,\n isConnecting,\n connectionSource,\n connectionSourcePort,\n hoveredPort,\n onPipeCreated,\n selectionEnabled,\n selectNode,\n onNodeClick,\n ]\n );\n\n const handlePipeClick = useCallback(\n (pipeId: string, event: React.MouseEvent) => {\n if (selectionEnabled) {\n selectPipe(pipeId, event.ctrlKey || event.metaKey);\n }\n onPipeClick?.(pipeId);\n },\n [selectionEnabled, selectPipe, onPipeClick]\n );\n\n const handleCanvasClick = (e: React.MouseEvent<SVGSVGElement>) => {\n // Don't clear selection if clicking on a waypoint or its parent elements\n const target = e.target as Element;\n\n // Check if target or any parent has the pipe-route-point class\n if (target.closest('.pipe-route-point')) {\n return;\n }\n\n if (selectionEnabled) {\n clearSelection();\n }\n setSelectedWaypoint(null);\n };\n\n const handleDragOver = useCallback(\n (event: React.DragEvent) => {\n if (!features.allowSymbolDrop) return;\n\n event.preventDefault();\n event.dataTransfer.dropEffect = 'copy';\n\n // Convert screen coordinates to world coordinates\n if (!svgRef.current) return;\n\n const worldPos = screenToWorld(event.clientX, event.clientY);\n\n setDragOverPosition(worldPos);\n },\n [features.allowSymbolDrop, screenToWorld, svgRef]\n );\n\n const handleDrop = useCallback(\n (event: React.DragEvent) => {\n if (!features.allowSymbolDrop) return;\n\n event.preventDefault();\n setDragOverPosition(null);\n\n // Get symbol ID from drag data\n const symbolId = event.dataTransfer.getData('application/symbol-id');\n if (!symbolId) return;\n\n // Convert drop position to world coordinates\n if (!svgRef.current) return;\n const worldPos = screenToWorld(event.clientX, event.clientY);\n\n onSymbolDrop?.(symbolId, worldPos);\n },\n [features.allowSymbolDrop, screenToWorld, onSymbolDrop, svgRef]\n );\n\n const handleDragLeave = useCallback(() => {\n setDragOverPosition(null);\n }, []);\n\n const handleMouseMove = useCallback(\n (event: React.MouseEvent<SVGSVGElement>) => {\n const connectionModeEnabled = features.connectionMode ?? false;\n\n if (connectionModeEnabled) {\n const worldPos = screenToWorld(event.clientX, event.clientY);\n\n // Update cursor position if connecting\n if (isConnecting && connectionSource) {\n setConnectionCursor(worldPos);\n }\n\n // Always track hovered port in connection mode (for both source and target selection)\n let foundPort: string | null = null;\n let foundNode: string | null = null;\n\n for (const node of localDiagram.nodes) {\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol || !symbol.ports) continue;\n\n for (const port of symbol.ports) {\n const portPos = getPortWorldPosition(node, port.id);\n if (!portPos) continue;\n\n const distance = Math.sqrt(\n (worldPos.x - portPos.x) ** 2 + (worldPos.y - portPos.y) ** 2\n );\n\n // Increased detection radius to 15 for better clickability (visual port is radius 5-8)\n if (distance < 15) {\n foundPort = port.id;\n foundNode = node.id;\n break;\n }\n }\n if (foundPort) break;\n }\n\n setHoveredPort(foundPort);\n setHoveredNode(foundNode);\n }\n },\n [isConnecting, connectionSource, screenToWorld, localDiagram.nodes, features.connectionMode]\n );\n\n const { name } = diagram;\n const viewBox = controlledViewBox;\n const displayDiagram = localDiagram;\n\n // Get first selected node for position panel\n const selectedNode =\n selectionEnabled && selection.selectedNodeIds.size === 1\n ? localDiagram.nodes.find((n) => selection.selectedNodeIds.has(n.id)) || null\n : null;\n\n return (\n <div\n ref={containerRef}\n className={`pid-canvas ${className}`.trim()}\n style={{\n width: '100%',\n height: '100%',\n position: 'relative',\n overflow: 'hidden',\n backgroundColor: '#f5f5f5',\n ...style,\n }}\n {...props}\n >\n <svg\n ref={svgRef}\n width=\"100%\"\n height=\"100%\"\n viewBox={`${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n style={{\n display: 'block',\n }}\n aria-label={`P&ID diagram: ${name}`}\n role=\"img\"\n onClick={handleCanvasClick}\n onMouseMove={handleMouseMove}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n onDragLeave={handleDragLeave}\n >\n {/* Background - solid color or grid */}\n {features.showGrid !== false ? (\n <>\n <defs>\n {/* Minor grid - 5 unit increments */}\n <pattern id=\"grid-minor\" width=\"5\" height=\"5\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M 5 0 L 0 0 0 5\" fill=\"none\" stroke=\"#e5e7eb\" strokeWidth=\"0.5\" />\n </pattern>\n {/* Major grid - 25 unit increments */}\n <pattern id=\"grid-major\" width=\"25\" height=\"25\" patternUnits=\"userSpaceOnUse\">\n <rect width=\"25\" height=\"25\" fill=\"url(#grid-minor)\" />\n <path d=\"M 25 0 L 0 0 0 25\" fill=\"none\" stroke=\"#d1d5db\" strokeWidth=\"1\" />\n </pattern>\n </defs>\n <rect\n x={Math.floor(viewBox.x / 25) * 25 - viewBox.width}\n y={Math.floor(viewBox.y / 25) * 25 - viewBox.height}\n width={Math.ceil((viewBox.width * 3) / 25) * 25}\n height={Math.ceil((viewBox.height * 3) / 25) * 25}\n fill=\"url(#grid-major)\"\n />\n </>\n ) : (\n <rect\n x={Math.floor(viewBox.x / 25) * 25 - viewBox.width}\n y={Math.floor(viewBox.y / 25) * 25 - viewBox.height}\n width={Math.ceil((viewBox.width * 3) / 25) * 25}\n height={Math.ceil((viewBox.height * 3) / 25) * 25}\n fill={features.backgroundColor ?? '#ffffff'}\n />\n )}\n\n {/* Render pipes (behind nodes for proper layering) */}\n <PipeRenderer\n pipes={displayDiagram.pipes}\n selectedPipeIds={selectionEnabled ? selection.selectedPipeIds : undefined}\n onPipeClick={selectionEnabled ? handlePipeClick : undefined}\n enableWaypointEditing={false}\n onWaypointDragStart={undefined}\n editingPipeId={null}\n draggingWaypointIndex={null}\n waypointDragOffset={undefined}\n onPipeSegmentClick={undefined}\n />\n\n {/* Render nodes */}\n <NodeRenderer\n nodes={displayDiagram.nodes}\n selectedNodeIds={selectionEnabled ? selection.selectedNodeIds : undefined}\n onNodeClick={\n onNodeClick\n ? handleNodeClick\n : selectionEnabled || features.connectionMode\n ? handleNodeClick\n : undefined\n }\n onNodeDoubleClick={selectionEnabled ? handleNodeDoubleClick : undefined}\n enableDrag={dragEnabled && !isConnecting}\n onNodeDragStart={dragEnabled && !isConnecting ? handleNodeDragStart : undefined}\n draggingNodeId={dragState.draggedId}\n dragOffset={dragState.offset}\n statusColorMap={statusColorMap}\n />\n\n {/* Render waypoint editing controls on top of nodes */}\n {(features.editPipeRoutes ?? false) && (\n <PipeRenderer\n pipes={displayDiagram.pipes}\n selectedPipeIds={selectionEnabled ? selection.selectedPipeIds : undefined}\n onPipeClick={undefined}\n enableWaypointEditing={true}\n onWaypointClick={handleWaypointClick}\n onWaypointDragStart={handleWaypointDragStart}\n selectedWaypointPipeId={selectedWaypoint?.pipeId || null}\n selectedWaypointIndex={selectedWaypoint?.pointIndex ?? null}\n editingPipeId={waypointDragState.draggedId?.split(':')[0] || null}\n draggingWaypointIndex={\n waypointDragState.draggedId\n ? parseInt(waypointDragState.draggedId.split(':')[1], 10)\n : null\n }\n waypointDragOffset={waypointDragState.offset}\n onPipeSegmentClick={handlePipeSegmentClick}\n />\n )}\n\n {/* Render telemetry data overlays */}\n {telemetryEnabled && telemetry && (\n <g id=\"layer-telemetry\">\n {displayDiagram.nodes.map((node) => {\n // Status LEDs bind to named signals and show no value badge.\n if (node.symbolId === 'status-indicator') return null;\n\n const binding = telemetry.get(node.id);\n if (!binding) return null;\n\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol) return null;\n\n // Position overlay centered horizontally and below the node by default\n const overlayX = node.transform.x + symbol.viewBox.width / 2;\n const overlayY = node.transform.y + symbol.viewBox.height + 50;\n\n return (\n <DataOverlay\n key={`telemetry-${node.id}`}\n nodeId={node.id}\n x={overlayX}\n y={overlayY}\n telemetry={binding}\n scale={features.telemetryScale ?? 1.0}\n statusColorMap={statusColorMap}\n />\n );\n })}\n </g>\n )}\n\n {/* Drop indicator */}\n {dragOverPosition && (\n <circle\n cx={dragOverPosition.x}\n cy={dragOverPosition.y}\n r={20}\n fill=\"#3b82f6\"\n opacity={0.3}\n stroke=\"#3b82f6\"\n strokeWidth={2}\n pointerEvents=\"none\"\n />\n )}\n\n {/* Connection mode rubber-band line */}\n {isConnecting && connectionSource && connectionCursor && (\n <g id=\"connection-preview\" pointerEvents=\"none\">\n {(() => {\n const sourceNode = displayDiagram.nodes.find((n) => n.id === connectionSource);\n if (!sourceNode) return null;\n\n // If a source port was selected, draw line from that port\n const symbol = getSymbolDefinition(sourceNode.symbolId);\n let lineStartX = sourceNode.transform.x;\n let lineStartY = sourceNode.transform.y;\n\n if (connectionSourcePort && symbol?.ports) {\n const port = symbol.ports.find((p) => p.id === connectionSourcePort);\n if (port) {\n const portPos = getPortWorldPosition(sourceNode, connectionSourcePort);\n if (portPos) {\n lineStartX = portPos.x;\n lineStartY = portPos.y;\n }\n }\n }\n\n return (\n <>\n {/* Highlight source port */}\n <circle\n cx={lineStartX}\n cy={lineStartY}\n r={30}\n fill=\"none\"\n stroke=\"#10b981\"\n strokeWidth={3}\n opacity={0.6}\n />\n {/* Rubber-band line */}\n <line\n x1={lineStartX}\n y1={lineStartY}\n x2={connectionCursor.x}\n y2={connectionCursor.y}\n stroke=\"#10b981\"\n strokeWidth={2}\n strokeDasharray=\"5,5\"\n opacity={0.8}\n />\n {/* Cursor indicator */}\n <circle\n cx={connectionCursor.x}\n cy={connectionCursor.y}\n r={8}\n fill=\"#10b981\"\n opacity={0.6}\n />\n </>\n );\n })()}\n </g>\n )}\n\n {/* Port indicators in connection mode */}\n {features.connectionMode && (\n <g id=\"port-indicators\">\n {displayDiagram.nodes.map((node) => {\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol?.ports) return null;\n\n return symbol.ports.map((port) => {\n const portPos = getPortWorldPosition(node, port.id);\n if (!portPos) return null;\n\n const isHovered = hoveredPort === port.id && hoveredNode === node.id;\n const isSourcePort =\n node.id === connectionSource && port.id === connectionSourcePort;\n\n return (\n <g key={`${node.id}-${port.id}`}>\n {/* Invisible larger hit area for easier clicking */}\n <circle\n cx={portPos.x}\n cy={portPos.y}\n r={15}\n fill=\"transparent\"\n style={{ cursor: 'pointer' }}\n pointerEvents=\"auto\"\n onClick={(e) => {\n e.stopPropagation();\n // Trigger the same logic as clicking on the node\n if (isConnecting && connectionSource) {\n // Second click - complete connection\n if (connectionSource !== node.id) {\n onPipeCreated?.(\n connectionSource,\n node.id,\n connectionSourcePort || undefined,\n port.id\n );\n setIsConnecting(false);\n setConnectionSource(null);\n setConnectionSourcePort(null);\n setConnectionCursor(null);\n setHoveredPort(null);\n }\n } else {\n // First click - start connection\n setIsConnecting(true);\n setConnectionSource(node.id);\n setConnectionSourcePort(port.id);\n }\n }}\n />\n {/* Visible port indicator */}\n <circle\n cx={portPos.x}\n cy={portPos.y}\n r={isHovered ? 8 : 5}\n fill={isSourcePort ? '#10b981' : isHovered ? '#3b82f6' : '#94a3b8'}\n stroke={isSourcePort ? '#059669' : isHovered ? '#2563eb' : '#64748b'}\n strokeWidth={isHovered || isSourcePort ? 2 : 1}\n opacity={isHovered || isSourcePort ? 1 : 0.6}\n pointerEvents=\"none\"\n />\n </g>\n );\n });\n })}\n </g>\n )}\n </svg>\n\n {/* Position Panel - shows when single node is selected */}\n {features.dragNodes && (\n <PositionPanel\n selectedNode={selectedNode}\n selectedWaypoint={\n selectedWaypoint\n ? {\n pipeId: selectedWaypoint.pipeId,\n pointIndex: selectedWaypoint.pointIndex,\n position: localDiagram.pipes.find((p) => p.id === selectedWaypoint.pipeId)\n ?.routePoints[selectedWaypoint.pointIndex] || { x: 0, y: 0 },\n }\n : null\n }\n onPositionChange={handlePositionChange}\n onWaypointPositionChange={handleWaypointPositionChange}\n position=\"top-right\"\n />\n )}\n </div>\n );\n}\n\n/**\n * Memo boundary - see PIDCanvasImpl doc comment (renderer-OOM fix). Shallow\n * compare: `diagram`, `telemetry`, `features`, `style`, `statusColorMap`, and\n * every callback are compared by identity; hosts that keep them stable skip\n * the full-canvas re-render entirely when unrelated screen state changes.\n */\nexport const PIDCanvas = React.memo(PIDCanvasImpl);\n","import React from 'react';\nimport type { NodeInstance } from '../../diagram/schema';\nimport type { NodeBadge, NodeBadgeCorner } from '../../diagram/schema/NodeBadge';\nimport { getSymbolDefinition, getAppearanceDefaults } from '../../diagram/symbols';\nimport { getBadgeGlyph } from '../../diagram/badges';\nimport { resolveStatusColor, normalizeDeviceStatus } from '../../theme/statusColors';\nimport type { StatusColorMap } from '../../theme/statusColors';\n\n// The unknown hatch reproduces the token's CSS equivalent:\n// `repeating-linear-gradient(135deg, transparent 0 5px, #8f8fc255 5px 7px)`.\n// A 7-unit tile holds a 5-unit background run and a 2-unit stripe, so the\n// same rhythm repeats every 7 user units along the diagonal.\nconst HATCH_TILE_SIZE = 7;\nconst HATCH_STRIPE_WIDTH = 2;\nconst HATCH_STRIPE_ANGLE = 135;\n// The token's stripe alpha, 0x55 of 0xff. Applied through `fill-opacity`,\n// not by appending hex digits to the colour. The stroke colour is a consumer\n// override point, so it may arrive as `rgb()`, a named colour, or an 8-digit\n// hex. Concatenating onto any of those yields an invalid colour, and the\n// stripe would silently paint black instead of the consumer's choice.\nconst HATCH_STRIPE_OPACITY = 0x55 / 0xff;\n\n/**\n * True when the system cannot confirm this node's state.\n *\n * Two triggers share one appearance (issue #27):\n * - `status: 'unknown'` marks an untrusted device (OPC UA severity Bad).\n * - `positionUnknown: true` marks a position the control system disclaims.\n *\n * Normalization goes through the shared helper, so this check cannot\n * disagree with the colour the symbol would otherwise be painted with.\n */\nfunction isUnknownNode(node: NodeInstance): boolean {\n return node.positionUnknown === true || normalizeDeviceStatus(node.status) === 'unknown';\n}\n\n// NodeBadge defaults (#33). A badge is a general slot: the consumer picks\n// every property. These are only the fallback when a property is omitted,\n// never a value the library imposes - see NodeBadge.ts for the one property\n// that has NO fallback (blinkPeriodMs).\nconst DEFAULT_BADGE_CORNER: NodeBadgeCorner = 'top-right';\nconst DEFAULT_BADGE_SIZE = 14; // disc diameter, local SVG units (matches the existing \"Forced\" corner badge's r=7 in DataOverlay)\nconst DEFAULT_BADGE_COLOR = '#111827'; // near-black, the TFFi first use\nconst DEFAULT_BADGE_BACKGROUND = '#d1d5db'; // light grey, the TFFi first use\nconst BADGE_GLYPH_BOX = 24; // glyphs are authored centered in a 24x24 local box\n\nconst BADGE_BLINK_KEYFRAME_ID = 'pid-node-badge-blink-keyframes';\nconst BADGE_BLINK_KEYFRAME_NAME = 'pid-node-badge-blink';\n\n/**\n * Injects the badge blink keyframe once into <head>, not per instance or\n * per render.\n *\n * DataOverlay's status dot used to blink via a SMIL `<animate\n * repeatCount=\"indefinite\">` and switched to a CSS keyframe because Blink's\n * SMIL timeline retains an indefinitely-repeating animation as a native\n * \"traced handle\" when its element unmounts mid-animation - a detached-node\n * leak under kiosk Chromium (see DataOverlay.tsx). A badge mounts and\n * unmounts with the condition it marks, so the same risk applies here. The\n * keyframe itself has no duration baked in - each badge's own\n * `blinkPeriodMs` supplies the duration via its inline `animation` style\n * (#33: the library must not hardcode a period).\n */\nfunction ensureBadgeBlinkKeyframes(): void {\n if (typeof document === 'undefined') return; // SSR guard\n if (document.getElementById(BADGE_BLINK_KEYFRAME_ID)) return; // idempotent\n const style = document.createElement('style');\n style.id = BADGE_BLINK_KEYFRAME_ID;\n style.textContent = `@keyframes ${BADGE_BLINK_KEYFRAME_NAME}{0%{opacity:1}50%{opacity:.15}100%{opacity:1}}`;\n document.head.appendChild(style);\n}\n\n/**\n * Badge disc center, in the symbol's own local (pre-transform) coordinates.\n * Centered ON the viewBox corner point, offset outward by a quarter of the\n * disc so most of it clears the symbol's own strokes while still reading as\n * attached to that corner (#33).\n */\nfunction badgeCenter(\n corner: NodeBadgeCorner,\n vb: { width: number; height: number },\n size: number\n): { x: number; y: number } {\n const margin = size / 4;\n switch (corner) {\n case 'top-left':\n return { x: -margin, y: -margin };\n case 'bottom-left':\n return { x: -margin, y: vb.height + margin };\n case 'bottom-right':\n return { x: vb.width + margin, y: vb.height + margin };\n case 'top-right':\n default:\n return { x: vb.width + margin, y: -margin };\n }\n}\n\n/**\n * Renders a node's corner badge (#33).\n *\n * Stacks on top of the symbol and its status color - it is a sibling\n * element, never a replacement for `--symbol-fill`/`--symbol-stroke`, so a\n * node can show `status: 'fault'` and a badge at the same time (the whole\n * reason this is a badge and not a body-color change).\n *\n * The badge counter-rotates like the node label does, so its glyph stays\n * upright regardless of the node's own rotation; its disc position still\n * swings with the node like the label's position does.\n */\nfunction renderNodeBadge(\n badge: NodeBadge,\n vb: { width: number; height: number },\n rotation: number\n): React.ReactElement {\n const glyphDef = getBadgeGlyph(badge.glyph);\n const size = badge.size ?? DEFAULT_BADGE_SIZE;\n const color = badge.color ?? DEFAULT_BADGE_COLOR;\n const backgroundColor = badge.backgroundColor ?? DEFAULT_BADGE_BACKGROUND;\n const corner = badge.corner ?? DEFAULT_BADGE_CORNER;\n const { x: cx, y: cy } = badgeCenter(corner, vb, size);\n const radius = size / 2;\n const glyphScale = size / BADGE_GLYPH_BOX;\n\n let blinkStyle: React.CSSProperties | undefined;\n if (badge.blink) {\n ensureBadgeBlinkKeyframes();\n blinkStyle = {\n animation: `${BADGE_BLINK_KEYFRAME_NAME} ${badge.blinkPeriodMs}ms ease-in-out infinite`,\n };\n }\n\n return (\n <g\n data-node-badge={badge.glyph}\n data-badge-corner={corner}\n data-badge-blink={badge.blink ? 'true' : 'false'}\n aria-label={badge.label}\n role={badge.label ? 'img' : undefined}\n transform={rotation ? `translate(${cx}, ${cy}) rotate(${-rotation})` : `translate(${cx}, ${cy})`}\n style={blinkStyle}\n >\n <circle r={radius} fill={backgroundColor} />\n {glyphDef && (\n <g\n color={color}\n transform={`scale(${glyphScale}) translate(${-BADGE_GLYPH_BOX / 2}, ${-BADGE_GLYPH_BOX / 2})`}\n dangerouslySetInnerHTML={{ __html: glyphDef.svgContent }}\n />\n )}\n </g>\n );\n}\n\nexport interface NodeRendererProps {\n /** Array of node instances to render */\n nodes: NodeInstance[];\n /** IDs of selected nodes */\n selectedNodeIds?: Set<string>;\n /** Callback when a node is clicked */\n onNodeClick?: (nodeId: string, event: React.MouseEvent) => void;\n /** Callback when a node is double-clicked */\n onNodeDoubleClick?: (nodeId: string, event: React.MouseEvent) => void;\n /** Callback when a node drag starts */\n onNodeDragStart?: (nodeId: string, event: React.MouseEvent | React.TouchEvent) => void;\n /** ID of node being dragged */\n draggingNodeId?: string | null;\n /** Drag offset for the dragging node */\n dragOffset?: { x: number; y: number };\n /** Whether dragging is enabled (default: false) */\n enableDrag?: boolean;\n /** Custom status-to-color map (overrides ISA-101 defaults) */\n statusColorMap?: StatusColorMap;\n}\n\n/**\n * NodeRenderer - Renders symbol instances with transforms and selection support\n *\n * Applies translate, rotate, and scale transforms to each node\n * Supports click selection with visual feedback\n *\n * Memoized (renderer-OOM fix): this layer holds the bulk of the SVG (the\n * dangerouslySetInnerHTML symbol bodies) and receives NO telemetry, so on a\n * telemetry-driven canvas re-render every prop is unchanged and the whole\n * layer bails out. Requires the callers' callbacks to be identity-stable\n * (PIDCanvas wraps its node handlers in useCallback for this).\n */\nfunction NodeRendererImpl({\n nodes,\n selectedNodeIds,\n onNodeClick,\n onNodeDoubleClick,\n onNodeDragStart,\n draggingNodeId,\n dragOffset = { x: 0, y: 0 },\n enableDrag = false,\n statusColorMap,\n}: NodeRendererProps): React.ReactElement {\n // A stable id per instance, so two canvases on one page never share or\n // clobber the hatch pattern's id.\n //\n // The id is stripped to letters, digits, underscore and dash. React 18 and\n // React 19 are both peer dependencies, and they return different `useId`\n // shapes: React 18 returns a value with colons, React 19 returns one with\n // underscores. The id goes into a `url(#...)` paint reference, so a\n // punctuation character in it is a risk this strip removes outright.\n const hatchPatternId = `node-unknown-hatch-${React.useId().replace(/[^a-zA-Z0-9_-]/g, '')}`;\n\n const visibleNodes = nodes.filter((node) => node.visible !== false); // Filter hidden nodes\n const hasUnknownNode = visibleNodes.some(isUnknownNode);\n // Resolved once for the whole canvas: the pattern is emitted once per\n // NodeRenderer instance, not once per node, so it needs one colour pair,\n // not each node's own resolved entry.\n const unknownEntry = resolveStatusColor('unknown', statusColorMap);\n\n return (\n <g id=\"layer-nodes\">\n {hasUnknownNode && (\n <defs>\n {/* Periwinkle hatch for an unconfirmed state (issue #27). The flat\n fill alone sits too close to the `off` fill to read as a\n separate state (see statusColors.ts), so the hatch is the\n non-colour signal that makes `unknown` legible. The background\n rect keeps the token's flat fill under the stripe, so the\n symbol still reads as the token colour with stripes on top. */}\n <pattern\n id={hatchPatternId}\n data-testid=\"unknown-hatch-pattern\"\n patternUnits=\"userSpaceOnUse\"\n width={HATCH_TILE_SIZE}\n height={HATCH_TILE_SIZE}\n patternTransform={`rotate(${HATCH_STRIPE_ANGLE})`}\n >\n <rect width={HATCH_TILE_SIZE} height={HATCH_TILE_SIZE} fill={unknownEntry.fill} />\n <rect\n x={HATCH_TILE_SIZE - HATCH_STRIPE_WIDTH}\n width={HATCH_STRIPE_WIDTH}\n height={HATCH_TILE_SIZE}\n fill={unknownEntry.stroke}\n fillOpacity={HATCH_STRIPE_OPACITY}\n />\n </pattern>\n </defs>\n )}\n {visibleNodes.map((node) => {\n const symbol = getSymbolDefinition(node.symbolId);\n const isSelected = selectedNodeIds?.has(node.id) ?? false;\n const isDragging = draggingNodeId === node.id;\n\n if (!symbol) {\n // Symbol not found - render placeholder\n return (\n <g key={node.id}>\n <circle\n cx={node.transform.x}\n cy={node.transform.y}\n r=\"15\"\n fill=\"#ff0000\"\n opacity=\"0.3\"\n stroke=\"#ff0000\"\n strokeWidth=\"2\"\n />\n <text\n x={node.transform.x}\n y={node.transform.y}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n fontSize=\"10\"\n fill=\"#ff0000\"\n >\n ?\n </text>\n </g>\n );\n }\n\n // Build transform string\n const transforms: string[] = [];\n\n // Apply drag offset if this node is being dragged\n const effectiveX = isDragging ? node.transform.x + dragOffset.x : node.transform.x;\n const effectiveY = isDragging ? node.transform.y + dragOffset.y : node.transform.y;\n\n // Translate\n transforms.push(`translate(${effectiveX}, ${effectiveY})`);\n\n // Rotate (around center of symbol)\n if (node.transform.rotation) {\n const centerX = symbol.viewBox.width / 2;\n const centerY = symbol.viewBox.height / 2;\n transforms.push(`rotate(${node.transform.rotation}, ${centerX}, ${centerY})`);\n }\n\n // Scale\n if (node.transform.scale && node.transform.scale !== 1) {\n const centerX = symbol.viewBox.width / 2;\n const centerY = symbol.viewBox.height / 2;\n transforms.push(\n `translate(${centerX}, ${centerY}) scale(${node.transform.scale}) translate(${-centerX}, ${-centerY})`\n );\n }\n\n // Flip\n if (node.transform.flipX || node.transform.flipY) {\n const scaleX = node.transform.flipX ? -1 : 1;\n const scaleY = node.transform.flipY ? -1 : 1;\n const centerX = symbol.viewBox.width / 2;\n const centerY = symbol.viewBox.height / 2;\n transforms.push(\n `translate(${centerX}, ${centerY}) scale(${scaleX}, ${scaleY}) translate(${-centerX}, ${-centerY})`\n );\n }\n\n // Determine colors: unknown hatch > customColors > statusColorMap > ISA-101 defaults.\n //\n // The unknown hatch wins over an explicit `customColors`. Nothing may\n // mask an unconfirmed state with a confident colour. A Status LED\n // gets `customColors.fill` from `useBooleanIndicators`, which reads\n // telemetry; when the device is untrusted, that telemetry is\n // untrusted too, so its colour must not replace the hatch.\n //\n // `--symbol-fill` is a valid target for an SVG `url(#id)` paint\n // reference, so pointing it at the pattern above reaches every\n // symbol without touching a definition.\n const statusEntry = resolveStatusColor(node.status, statusColorMap);\n const isUnknown = isUnknownNode(node);\n const fillColor = isUnknown\n ? `url(#${hatchPatternId})`\n : (node.customColors?.fill ?? statusEntry.fill);\n const strokeColor = isUnknown\n ? unknownEntry.stroke\n : (node.customColors?.stroke ?? statusEntry.stroke);\n\n // Appearance schema (per-element editable properties): schema defaults\n // merged under the node's own metadata overrides.\n const appearance = { ...getAppearanceDefaults(node.symbolId), ...(node.metadata ?? {}) };\n // CSS custom properties + filter, built as a plain record and spread\n // into the group style (custom props are not typed on CSSProperties).\n const ledStyle: Record<string, string | number> = {};\n if (appearance.shape !== undefined) {\n ledStyle['--led-rx'] = appearance.shape === 'circle' ? '50%' : '2';\n ledStyle['--led-rotate'] = appearance.shape === 'diamond' ? '45deg' : '0deg';\n }\n if (appearance.led !== undefined) {\n ledStyle['--led-bezel'] = appearance.led ? '2' : '0';\n }\n if (appearance.glow) {\n // A `url(#...)` paint reference is not a valid drop-shadow colour,\n // so an unknown node glows in the unknown stroke colour instead.\n const glowColor = isUnknown ? unknownEntry.stroke : fillColor;\n ledStyle.filter = `drop-shadow(0 0 4px ${glowColor})`;\n }\n const ledAnimation =\n typeof appearance.animation === 'string' && appearance.animation !== 'none'\n ? appearance.animation\n : undefined;\n\n // Label position (appearance-driven): 'bottom' default matches the\n // previous fixed layout, so unconfigured symbols are unchanged.\n const vb = symbol.viewBox;\n const ox = node.labelOffset?.x ?? 0;\n const oy = node.labelOffset?.y ?? 0;\n const labelPos =\n typeof appearance.labelPosition === 'string' ? appearance.labelPosition : 'bottom';\n let labelX = vb.width / 2 + ox;\n let labelY = vb.height + 12 + oy;\n let labelAnchor: 'start' | 'middle' | 'end' = 'middle';\n if (labelPos === 'top') {\n labelX = vb.width / 2 + ox;\n labelY = -6 + oy;\n labelAnchor = 'middle';\n } else if (labelPos === 'left') {\n labelX = -6 + ox;\n labelY = vb.height / 2 + oy;\n labelAnchor = 'end';\n } else if (labelPos === 'right') {\n labelX = vb.width + 6 + ox;\n labelY = vb.height / 2 + oy;\n labelAnchor = 'start';\n }\n\n return (\n <g\n key={node.id}\n data-node-id={node.id}\n data-symbol-id={node.symbolId}\n data-selected={isSelected}\n data-dragging={isDragging}\n data-led-animation={ledAnimation}\n data-position-unknown={node.positionUnknown === true ? 'true' : undefined}\n transform={transforms.join(' ')}\n style={\n {\n '--symbol-fill': fillColor,\n '--symbol-stroke': strokeColor,\n cursor: enableDrag\n ? isDragging\n ? 'grabbing'\n : 'grab'\n : onNodeClick\n ? 'pointer'\n : 'default',\n ...ledStyle,\n } as React.CSSProperties\n }\n onClick={(e) => {\n // Always fire onClick - drag system will handle whether to drag\n e.stopPropagation();\n onNodeClick?.(node.id, e);\n }}\n onDoubleClick={(e) => {\n e.stopPropagation();\n onNodeDoubleClick?.(node.id, e);\n }}\n onMouseDown={(e) => {\n if (enableDrag) {\n // Don't stop propagation - let click still fire\n onNodeDragStart?.(node.id, e);\n }\n }}\n onTouchStart={(e) => {\n if (enableDrag) {\n e.stopPropagation();\n onNodeDragStart?.(node.id, e);\n }\n }}\n >\n {/* Selection indicator - bounding box */}\n {isSelected && (\n <rect\n x=\"-5\"\n y=\"-5\"\n width={symbol.viewBox.width + 10}\n height={symbol.viewBox.height + 10}\n fill=\"none\"\n stroke={isDragging ? '#10b981' : '#3b82f6'}\n strokeWidth={isDragging ? '3' : '2'}\n strokeDasharray=\"4 2\"\n rx=\"2\"\n pointerEvents=\"none\"\n />\n )}\n\n {/* Render symbol inline (future: use <use> with <defs>) */}\n <g\n dangerouslySetInnerHTML={{ __html: symbol.svgContent }}\n data-symbol-content={symbol.id}\n style={{ pointerEvents: 'auto' }}\n />\n\n {/* Node label */}\n <text\n x={labelX}\n y={labelY}\n textAnchor={labelAnchor}\n fontSize={node.labelFontSize ?? 10}\n fontWeight=\"600\"\n fill={isSelected ? '#3b82f6' : '#1f2937'}\n transform={\n node.transform.rotation\n ? `rotate(${-node.transform.rotation}, ${labelX}, ${labelY})`\n : undefined\n }\n >\n {node.label || node.id}\n </text>\n\n {/* Corner badge (#33): a condition that is NOT a health severity.\n Renders last so it is never occluded by the symbol or label,\n and never touches --symbol-fill/--symbol-stroke above, so it\n stacks on every status, including 'fault'. */}\n {node.badge && renderNodeBadge(node.badge, vb, node.transform.rotation)}\n </g>\n );\n })}\n </g>\n );\n}\n\n/** Memo boundary - see NodeRendererImpl doc comment (renderer-OOM fix). */\nexport const NodeRenderer = React.memo(NodeRendererImpl);\n","import type { SymbolDefinition } from '../schema';\n\n/**\n * Mock symbol library with basic P&ID shapes\n * Phase 4 will add proper symbol library management\n */\nexport const mockSymbolLibrary: Record<string, SymbolDefinition> = {\n 'valve-gate': {\n id: 'valve-gate',\n name: 'Gate Valve (Powered)',\n category: 'valve',\n viewBox: { x: 0, y: 0, width: 26, height: 26 },\n ports: [\n { id: 'inlet', x: 0, y: 18, type: 'inlet', direction: 'in' },\n { id: 'outlet', x: 26, y: 18, type: 'outlet', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Actuator box -->\n <rect x=\"16.74\" y=\"0\" width=\"18.02\" height=\"18.02\" fill=\"var(--symbol-fill, #edeeef)\" stroke=\"var(--symbol-stroke, #666666)\" pointer-events=\"all\"/>\n <!-- Actuator stem -->\n <path d=\"M 25.75 18.02 L 25.75 36.05\" fill=\"none\" stroke=\"var(--symbol-stroke, #666666)\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" pointer-events=\"all\"/>\n <!-- Valve body (two triangles forming diamond) -->\n <path d=\"M 0 20.6 L 25.75 36.05 L 0 51.5 Z M 51.5 20.6 L 25.75 36.05 L 51.5 51.5 Z\" fill=\"var(--symbol-fill, #edeeef)\" stroke=\"var(--symbol-stroke, #666666)\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" pointer-events=\"all\"/>\n </g>\n `,\n metadata: {\n description: 'Gate valve with powered actuator',\n tags: ['valve', 'gate', 'powered', 'actuator'],\n },\n },\n\n 'status-indicator': {\n id: 'status-indicator',\n name: 'Status LED',\n category: 'instrument',\n viewBox: { x: 0, y: 0, width: 24, height: 24 },\n ports: [],\n svgContent: `\n <rect x=\"2\" y=\"2\" width=\"20\" height=\"20\"\n rx=\"var(--led-rx, 50%)\"\n fill=\"var(--symbol-fill, #64748b)\"\n stroke=\"rgba(255,255,255,0.4)\"\n stroke-width=\"var(--led-bezel, 0)\"\n style=\"transform-box: fill-box; transform: rotate(var(--led-rotate, 0deg)); transform-origin: center; pointer-events: all;\"/>\n `,\n metadata: {\n description: 'Status LED indicator driven by telemetry status color',\n tags: ['status', 'led', 'indicator', 'instrument'],\n },\n },\n\n 'pump-positive-displacement': {\n id: 'pump-positive-displacement',\n name: 'Positive Displacement Pump',\n category: 'pump',\n viewBox: { x: 0, y: 0, width: 28.125, height: 28.125 },\n ports: [\n { id: 'inlet', x: 0, y: 14.0625, type: 'inlet', direction: 'in' },\n { id: 'outlet', x: 28.125, y: 14.0625, type: 'outlet', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.28125)\">\n <!-- Outer circle -->\n <ellipse \n cx=\"50\" \n cy=\"50\" \n rx=\"50\" \n ry=\"50\" \n fill=\"var(--symbol-fill, #edeeef)\"\n stroke=\"var(--symbol-stroke, rgb(0, 0, 0))\"\n stroke-width=\"1\"\n />\n \n <!-- Diagonal line from top to bottom-right -->\n <path \n d=\"M 50 0 L 100 50 L 50 100\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, rgb(0, 0, 0))\"\n stroke-width=\"1\"\n stroke-miterlimit=\"10\"\n />\n \n <!-- Center square -->\n <rect \n x=\"35\" \n y=\"35\" \n width=\"30\" \n height=\"30\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, rgb(0, 0, 0))\"\n stroke-width=\"1\"\n />\n </g>\n `,\n metadata: {\n description: 'Positive displacement pump (ISO standard)',\n tags: ['pump', 'positive-displacement', 'iso'],\n },\n },\n\n 'vannefold-bottom': {\n id: 'vannefold-bottom',\n name: 'Vannefold Block (Bottom)',\n category: 'valve',\n viewBox: { x: -0.25, y: -0.25, width: 44, height: 33.892578 },\n ports: [\n { id: 'bottom-inlet', x: 0, y: 12.5, type: 'control', direction: 'in' },\n { id: 'top-outlet', x: 24, y: 32.5, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Bottom inlet pipe -->\n <path \n d=\"M 3,29.785156 H 0 v -10 h 3 m 14,10 h 3 v -10 h -3 m -15,8 h 16 m -16,-6 h 16\"\n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Top outlet pipe -->\n <path \n d=\"m 43,49.785156 v -3 h 10 v 3 m -10,14 v 3 h 10 v -3 m -8,-15 v 16 m 6,-16 v 16\"\n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"7\" \n y=\"0\" \n width=\"80\" \n height=\"59.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"9\" \n y=\"2\" \n width=\"76\" \n height=\"55.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"15\" cy=\"7\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"7\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"7\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"7\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"51.785156\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"51.785156\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"51.785156\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"51.785156\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n </g>\n `,\n metadata: {\n description: 'Vannefold heat exchanger block with bottom inlet configuration',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'bottom'],\n },\n },\n\n 'vannefold-middle': {\n id: 'vannefold-middle',\n name: 'Vannefold Block (Middle)',\n category: 'valve',\n viewBox: { x: -0.25, y: -0.25, width: 47.5, height: 26.548828 },\n ports: [\n { id: 'left-inlet', x: 0, y: 13.65625, type: 'control', direction: 'in' },\n { id: 'right-outlet', x: 47.5, y: 13.65625, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Left inlet pipe -->\n <path \n d=\"M 3,22.3125 H 0 v 10 H 3 m 14,-10 h 3 v 10 h -3 m -15,-8 h 16 m -16,6 h 16\"\n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"7\" \n y=\"0\" \n width=\"80\" \n height=\"52.097656\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"9\" \n y=\"0\" \n width=\"76\" \n height=\"52.097656\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"15\" cy=\"5.3125\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"5.3125\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"5.3125\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"5.3125\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"47.3125\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"47.3125\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"47.3125\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"47.3125\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n </g>\n `,\n metadata: {\n description: 'Vannefold heat exchanger block with side inlet configuration',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'middle'],\n },\n },\n\n 'vannefold-top': {\n id: 'vannefold-top',\n name: 'Vannefold Block (Top)',\n category: 'valve',\n viewBox: { x: -0.25, y: -0.25, width: 47.5, height: 30.392578 },\n ports: [\n { id: 'bottom-inlet', x: 0, y: 17.5, type: 'control', direction: 'in' },\n { id: 'top-outlet', x: 24, y: 0, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Bottom inlet pipe -->\n <path \n d=\"M 3,30 H 0 V 40 H 3 M 17,30 h 3 V 40 H 17 M 2,32 H 18 M 2,38 H 18\"\n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"7\" \n y=\"0\" \n width=\"80\" \n height=\"59.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"9\" \n y=\"2\" \n width=\"76\" \n height=\"57.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"15\" cy=\"8\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"8\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"8\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"8\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"55\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"55\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"55\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"55\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n </g>\n `,\n metadata: {\n description: 'Vannefold heat exchanger block with bottom inlet and top outlet configuration',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'top'],\n },\n },\n\n 'vannefold-top-top-inlet': {\n id: 'vannefold-top-top-inlet',\n name: 'Vannefold Block (Top, Top Inlet)',\n category: 'valve',\n viewBox: { x: 0, y: 0, width: 44, height: 33.788002 },\n ports: [\n { id: 'top-inlet', x: 23.5, y: 0, type: 'control', direction: 'in' },\n { id: 'bottom-outlet', x: 0, y: 21, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Top inlet pipe flange -->\n <path \n d=\"M 52,3 V 1.3598666e-8 H 42 V 3 m 10,14 v 3 H 42 V 17 M 50,2 V 18 M 44,2 v 16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Bottom outlet pipe flange -->\n <path \n d=\"M 3,36.79085 H 0 v 10 h 3 m 14,-10 h 3 v 10 h -3 m -15,-8 h 16 m -16,6 h 16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"7\" \n y=\"6.7908498\" \n width=\"80\" \n height=\"59.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"9\" \n y=\"8.7908498\" \n width=\"76\" \n height=\"57.785156\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"15\" cy=\"14.79085\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"14.79085\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"61.79085\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"61.79085\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"14.79085\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"14.79085\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"61.79085\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"79\" cy=\"61.79085\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n </g>\n `,\n metadata: {\n description: 'Vannefold heat exchanger block with top inlet and bottom outlet configuration',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'top', 'top-inlet'],\n },\n },\n\n 'vannefold-left': {\n id: 'vannefold-left',\n name: 'Vannefold Block (Left)',\n category: 'valve',\n viewBox: { x: 0, y: 0, width: 33.892578, height: 44.25 },\n ports: [\n { id: 'left-inlet', x: 0, y: 20.75, type: 'control', direction: 'in' },\n { id: 'right-outlet', x: 21, y: 43.5, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Left inlet pipe flange -->\n <path \n d=\"M 3,46.5 H 2.8789924e-8 v -10 H 3 m 14,10 h 3 v -10 h -3 m -15,8 h 16 m -16,-6 h 16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Right outlet pipe flange -->\n <path \n d=\"m 37,70.5 v -3 h 10 v 3 m -10,14 v 3 h 10 v -3 m -8,-15 v 16 m 6,-16 v 16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"7\" \n y=\"0.5\" \n width=\"59.785156\" \n height=\"80\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"9\" \n y=\"2.5\" \n width=\"57.785156\" \n height=\"76\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"15\" cy=\"8.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"8.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"62\" cy=\"8.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"62\" cy=\"8.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"72.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"15\" cy=\"72.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"62\" cy=\"72.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"62\" cy=\"72.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n `,\n metadata: {\n description: 'Vannefold heat exchanger block with left inlet and right outlet, vertical orientation',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'left', 'vertical'],\n },\n },\n \n 'vannefold-right': {\n id: 'vannefold-right',\n name: 'Vannefold Block (Right)',\n category: 'valve',\n viewBox: { x: 0, y: 0, width: 33.892578, height: 44.25 },\n ports: [\n { id: 'right-inlet', x: 33.892578, y: 20.75, type: 'control', direction: 'in' },\n { id: 'left-outlet', x: 12.392578, y: 43.75, type: 'control', direction: 'out' },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Right inlet pipe flange -->\n <path \n d=\"m 63.785156,46.5 h 3 v -10 h -3 m -14,10 h -3 v -10 h 3 m 15,8 h -16 m 16,-6 h -16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Left outlet pipe flange -->\n <path \n d=\"m 29.785156,70.5 v -3 h -10 v 3 m 10,14 v 3 h -10 v -3 m 8,-15 v 16 m -6,-16 v 16\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n />\n \n <!-- Main block outer -->\n <rect \n x=\"0\" \n y=\"0.5\" \n width=\"59.785156\" \n height=\"80\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Main block inner -->\n <rect \n x=\"0\" \n y=\"2.5\" \n width=\"57.785156\" \n height=\"76\" \n fill=\"var(--symbol-fill, #eaeaea)\" \n stroke=\"var(--symbol-stroke, #000000)\"\n />\n \n <!-- Bolt holes -->\n <ellipse cx=\"51.785156\" cy=\"8.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"51.785156\" cy=\"8.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"4.785156\" cy=\"8.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"4.785156\" cy=\"8.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"51.785156\" cy=\"72.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"51.785156\" cy=\"72.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"4.785156\" cy=\"72.5\" rx=\"5\" ry=\"5\" fill=\"#ffffff\" stroke=\"var(--symbol-stroke, #000000)\" />\n <ellipse cx=\"4.785156\" cy=\"72.5\" rx=\"3\" ry=\"3\" fill=\"none\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n `,\n metadata: {\n description: 'Vannefold heat exchanger block with right inlet and left outlet, vertical orientation',\n tags: ['heat-exchanger', 'vannefold', 'fold', 'right', 'vertical'],\n },\n },\n\n 'pixer': {\n id: 'pixer',\n name: 'Pixer',\n category: 'pump',\n viewBox: { x: 0, y: 0, width: 25.3125, height: 23.34375 },\n ports: [\n { id: 'right-outlet', x: 25.3125, y: 15.46875, type: 'control', direction: 'out' },\n { id: 'left-outlet', x: 0, y: 15.46875, type: 'control', direction: 'out' },\n { id: 'left-inlet', x: 25.3125, y: 9.84375, type: 'control', direction: 'in' },\n { id: 'right-inlet', x: 0, y: 9.84375, type: 'control', direction: 'in' },\n { id: 'top-inlet', x: 12.65625, y: 0, type: 'control', direction: 'in' },\n ],\n svgContent: `\n <g transform=\"scale(0.0984375) translate(-195, -186)\">\n <!-- Top mounting flange -->\n <rect \n width=\"141.313\" \n height=\"9.872\" \n transform=\"translate(252.392 185.89)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Motor housing top -->\n <rect \n width=\"106.614\" \n height=\"2.468\" \n transform=\"translate(269.742 199.918)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"106.614\" \n height=\"5.835\" \n transform=\"translate(269.742 202.384)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"106.614\" \n height=\"2.468\" \n transform=\"translate(269.742 208.222)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Bottom mounting flange -->\n <rect \n width=\"221.702\" \n height=\"2.401\" \n transform=\"translate(212.199 395.587)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"221.702\" \n height=\"6.269\" \n transform=\"translate(212.199 397.988)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"221.702\" \n height=\"2.65\" \n transform=\"translate(212.199 404.257)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Motor housing transition -->\n <path \n d=\"M444.7,228.776l17.117,4.1H568.429l17.089-4.085a.032.032,0,0,0,0-.062H444.705a.028.028,0,0,0-.007.046Z\" \n transform=\"translate(-192.059 -32.959)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Bottom flange -->\n <rect \n width=\"256\" \n height=\"9.872\" \n transform=\"translate(195.05 411.066)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n <path \n d=\"M195.05,1148.286l17.149-4.156H433.9l17.149,4.156Z\" \n transform=\"translate(0 -737.221)\" \n fill=\"var(--symbol-fill, #e2e2e2)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n \n <!-- Vessel top cone -->\n <path \n d=\"M451.05,328.356h-256l74.692-34.946H376.358Z\" \n transform=\"translate(0 -82.72)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Main vessel body -->\n <rect \n width=\"256\" \n height=\"59.803\" \n transform=\"translate(195.05 245.636)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n <rect \n width=\"256\" \n height=\"76.892\" \n transform=\"translate(195.05 308.417)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Vessel bottom transitions -->\n <path \n d=\"M449.265,705.7H196.773l-1.654-1.465a.014.014,0,0,1,0-.023H451.078Z\" \n transform=\"translate(-0.049 -398.769)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <path \n d=\"M449.278,710.66H196.793l-1.663,1.462v.016H451.086s.012,0,0-.016Z\" \n transform=\"translate(-0.062 -403.731)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <path \n d=\"M433.9,1060.758H212.2L195.05,1050.48h256Z\" \n transform=\"translate(0 -665.171)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Agitator blades/impellers (simplified without gradients) -->\n <ellipse \n cx=\"15.454\" \n cy=\"6.096\" \n rx=\"15.454\" \n ry=\"6.096\" \n transform=\"translate(307.486 228.161)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #c0c0c0)\"\n />\n <ellipse \n cx=\"15.454\" \n cy=\"5.3\" \n rx=\"15.454\" \n ry=\"5.3\" \n transform=\"translate(307.486 229.753)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <ellipse \n cx=\"5.174\" \n cy=\"15.117\" \n rx=\"5.174\" \n ry=\"15.117\" \n transform=\"translate(367.144 236.079) rotate(-77.61)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M947,380.271s1.631-2.83,8.084-2.235c5.427.5,16.927,2.855,20.574,8.26\" \n transform=\"translate(-578.512 -147.764)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"15.117\" \n cy=\"5.174\" \n rx=\"15.117\" \n ry=\"5.174\" \n transform=\"translate(247 232.438) rotate(-12.39)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M456.439,380.271s-1.631-2.83-8.073-2.235c-5.43.5-16.93,2.855-20.576,8.26\" \n transform=\"translate(-179.058 -147.764)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"1.651\" \n cy=\"15.082\" \n rx=\"1.651\" \n ry=\"15.082\" \n transform=\"translate(406.71 230.174) rotate(-68.13)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M1119,375.62a119.876,119.876,0,0,1,26.193,10.419\" \n transform=\"translate(-710.84 -145.969)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"15.082\" \n cy=\"1.651\" \n rx=\"15.082\" \n ry=\"1.651\" \n transform=\"translate(210.225 238.342) rotate(-21.87)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M293.943,375.62a119.829,119.829,0,0,0-26.193,10.419\" \n transform=\"translate(-55.932 -145.969)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n </g>\n `,\n metadata: {\n description: 'Pixer mixer/agitator vessel with top-mounted drive',\n tags: ['vessel', 'mixer', 'agitator', 'pixer', 'tank'],\n },\n },\n\n 'pixer-barb': {\n id: 'pixer-barb',\n name: 'Pixer (Hose Barb)',\n category: 'pump',\n viewBox: { x: 0, y: 0, width: 25.3125, height: 23.34375 },\n ports: [\n { id: 'right-outlet', x: 25.3125, y: 15.46875, type: 'control', direction: 'out' },\n { id: 'left-outlet', x: 0, y: 15.46875, type: 'control', direction: 'out' },\n { id: 'left-inlet', x: 25.3125, y: 9.84375, type: 'control', direction: 'in' },\n { id: 'right-inlet', x: 0, y: 9.84375, type: 'control', direction: 'in' },\n { id: 'top-inlet', x: 12.65625, y: 0, type: 'control', direction: 'in' },\n ],\n svgContent: `\n <g transform=\"scale(0.0984375) translate(-195, -186)\">\n <!-- Hose barb ridges at top -->\n <rect \n width=\"20\" \n height=\"2\" \n transform=\"translate(313 173)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"0.5\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <rect \n width=\"20\" \n height=\"2\" \n transform=\"translate(313 177)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"0.5\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <rect \n width=\"20\" \n height=\"2\" \n transform=\"translate(313 181)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"0.5\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n \n <!-- Taper from barb to motor housing -->\n <path \n d=\"M 313,183 L 269.742,199.918 L 376.356,199.918 L 333,183 Z\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n \n <!-- Motor housing top -->\n <rect \n width=\"106.614\" \n height=\"2.468\" \n transform=\"translate(269.742 199.918)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"106.614\" \n height=\"5.835\" \n transform=\"translate(269.742 202.384)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"106.614\" \n height=\"2.468\" \n transform=\"translate(269.742 208.222)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Bottom mounting flange -->\n <rect \n width=\"221.702\" \n height=\"2.401\" \n transform=\"translate(212.199 395.587)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"221.702\" \n height=\"6.269\" \n transform=\"translate(212.199 397.988)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <rect \n width=\"221.702\" \n height=\"2.65\" \n transform=\"translate(212.199 404.257)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Bottom flange -->\n <rect \n width=\"256\" \n height=\"9.872\" \n transform=\"translate(195.05 411.066)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n <path \n d=\"M195.05,1148.286l17.149-4.156H433.9l17.149,4.156Z\" \n transform=\"translate(0 -737.221)\" \n fill=\"var(--symbol-fill, #e2e2e2)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n \n <!-- Vessel top cone -->\n <path \n d=\"M451.05,328.356h-256l74.692-34.946H376.358Z\" \n transform=\"translate(0 -82.72)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Main vessel body -->\n <rect \n width=\"256\" \n height=\"59.803\" \n transform=\"translate(195.05 245.636)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n <rect \n width=\"256\" \n height=\"76.892\" \n transform=\"translate(195.05 308.417)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Vessel bottom transitions -->\n <path \n d=\"M449.265,705.7H196.773l-1.654-1.465a.014.014,0,0,1,0-.023H451.078Z\" \n transform=\"translate(-0.049 -398.769)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <path \n d=\"M449.278,710.66H196.793l-1.663,1.462v.016H451.086s.012,0,0-.016Z\" \n transform=\"translate(-0.062 -403.731)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n <path \n d=\"M433.9,1060.758H212.2L195.05,1050.48h256Z\" \n transform=\"translate(0 -665.171)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Agitator blades/impellers (simplified without gradients) -->\n <ellipse \n cx=\"15.454\" \n cy=\"6.096\" \n rx=\"15.454\" \n ry=\"6.096\" \n transform=\"translate(307.486 228.161)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #c0c0c0)\"\n />\n <ellipse \n cx=\"15.454\" \n cy=\"5.3\" \n rx=\"15.454\" \n ry=\"5.3\" \n transform=\"translate(307.486 229.753)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <ellipse \n cx=\"5.174\" \n cy=\"15.117\" \n rx=\"5.174\" \n ry=\"15.117\" \n transform=\"translate(367.144 236.079) rotate(-77.61)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M947,380.271s1.631-2.83,8.084-2.235c5.427.5,16.927,2.855,20.574,8.26\" \n transform=\"translate(-578.512 -147.764)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"15.117\" \n cy=\"5.174\" \n rx=\"15.117\" \n ry=\"5.174\" \n transform=\"translate(247 232.438) rotate(-12.39)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M456.439,380.271s-1.631-2.83-8.073-2.235c-5.43.5-16.93,2.855-20.576,8.26\" \n transform=\"translate(-179.058 -147.764)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"1.651\" \n cy=\"15.082\" \n rx=\"1.651\" \n ry=\"15.082\" \n transform=\"translate(406.71 230.174) rotate(-68.13)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M1119,375.62a119.876,119.876,0,0,1,26.193,10.419\" \n transform=\"translate(-710.84 -145.969)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n <ellipse \n cx=\"15.082\" \n cy=\"1.651\" \n rx=\"15.082\" \n ry=\"1.651\" \n transform=\"translate(210.225 238.342) rotate(-21.87)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #d0d0d0)\"\n />\n <path \n d=\"M293.943,375.62a119.829,119.829,0,0,0-26.193,10.419\" \n transform=\"translate(-55.932 -145.969)\" \n fill=\"none\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\"\n />\n </g>\n `,\n metadata: {\n description: 'Pixer mixer/agitator vessel with tapered hose barb connection at top',\n tags: ['vessel', 'mixer', 'agitator', 'pixer', 'tank', 'hose-barb'],\n },\n },\n \n 'vessel-balloon': {\n id: 'vessel-balloon',\n name: 'Vessel',\n category: 'vessel',\n viewBox: { x: 0, y: 0, width: 25.3125, height: 45.5 },\n ports: [\n { id: 'top', type: 'inlet', x: 12.65625, y: 0 },\n { id: 'left', type: 'inlet', x: 2.5, y: 19.5 },\n { id: 'right', type: 'outlet', x: 22.8125, y: 19.5 },\n { id: 'bottom', type: 'outlet', x: 12.65625, y: 45.5 },\n ],\n svgContent: `\n <g transform=\"scale(0.0984375) translate(-195, -100)\">\n <!-- Top opening nozzle -->\n <rect \n width=\"65\" \n height=\"19.5\" \n transform=\"translate(290.5, 100)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Transition from nozzle to dome -->\n <path \n d=\"M 290.5,119.5 L 238,152 L 408,152 L 355.5,119.5 Z\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Upper balloon dome - rounded bulge -->\n <path \n d=\"M 238,152 C 185,210 150,320 180,395 L 466,395 C 496,320 461,210 408,152 Z\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Tapered vessel body -->\n <path \n d=\"M 180,395 L 269.742,530 L 376.356,530 L 466,395 Z\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n \n <!-- Bottom cone -->\n <path \n d=\"M 269.742,530 L 280,555 L 366,555 L 376.356,530 Z\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\"\n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #e0e0e0)\"\n />\n \n <!-- Bottom flange matching pixer top dimensions -->\n <rect \n width=\"141.313\" \n height=\"9.872\" \n transform=\"translate(252.392, 555)\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-miterlimit=\"10\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #eaeaea)\"\n />\n </g>\n `,\n metadata: {\n description: 'Balloon-shaped vessel with rounded top and tapered bottom, designed to fit on standard pixer',\n tags: ['vessel', 'storage', 'tank', 'balloon'],\n },\n },\n\n 'hollow-fiber': {\n id: 'hollow-fiber',\n name: 'Hollow Fiber',\n category: 'other',\n viewBox: { x: 0, y: 0, width: 30, height: 109 },\n ports: [\n { id: 'top-left', type: 'outlet', x: 10, y: 2.75 },\n { id: 'top-right', type: 'outlet', x: 24.5, y: 22.25 },\n { id: 'bottom-left', type: 'inlet', x: 10, y: 106.25 },\n { id: 'bottom-right', type: 'inlet', x: 24.5, y: 87.25 },\n ],\n svgContent: `\n <g transform=\"scale(0.5)\">\n <!-- Top left flange -->\n <rect x=\"10.5\" y=\"0.5\" width=\"20\" height=\"10\" rx=\"1.5\" ry=\"1.5\" fill=\"var(--symbol-fill, #eaeaea)\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n <!-- Top right flange -->\n <rect x=\"39.5\" y=\"39.5\" width=\"20\" height=\"10\" rx=\"1.5\" ry=\"1.5\" fill=\"var(--symbol-fill, #eaeaea)\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n <!-- Bottom left flange -->\n <rect x=\"10.5\" y=\"207.5\" width=\"20\" height=\"10\" rx=\"1.5\" ry=\"1.5\" fill=\"var(--symbol-fill, #eaeaea)\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n <!-- Bottom right flange -->\n <rect x=\"39.5\" y=\"169.5\" width=\"20\" height=\"10\" rx=\"1.5\" ry=\"1.5\" fill=\"var(--symbol-fill, #eaeaea)\" stroke=\"var(--symbol-stroke, #000000)\" />\n \n <!-- Main vessel body -->\n <rect x=\"0.5\" y=\"9.5\" width=\"40\" height=\"199.5\" rx=\"6\" ry=\"6\" fill=\"var(--symbol-fill, #eaeaea)\" stroke=\"var(--symbol-stroke, #000000)\" />\n </g>\n `,\n metadata: {\n description: 'Hollow fiber filter module with multiple inlet/outlet connections',\n tags: ['filter', 'hollow-fiber', 'filtration', 'separation', 'membrane'],\n },\n },\n \n 'psr-f': {\n id: 'psr-f',\n name: 'PSR F',\n category: 'instrument',\n viewBox: { x: 0, y: 0, width: 28.5, height: 28.5 },\n ports: [\n { id: 'top', type: 'signal', x: 14.25, y: 7.125 },\n { id: 'right', type: 'signal', x: 21.375, y: 14.25 },\n { id: 'bottom', type: 'signal', x: 14.25, y: 21.375 },\n { id: 'left', type: 'signal', x: 7.125, y: 14.25 },\n ],\n svgContent: `\n <g transform=\"scale(0.25) translate(28.5, 28.5)\">\n <!-- Circle background -->\n <ellipse \n cx=\"28.5\" \n cy=\"28.5\" \n rx=\"28.5\" \n ry=\"28.5\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #ffffff)\"\n />\n \n <!-- Letter F -->\n <text \n x=\"28.5\" \n y=\"38\" \n text-anchor=\"middle\" \n font-family=\"'Times New Roman', Georgia, serif\" \n font-size=\"34\" \n font-weight=\"normal\" \n font-style=\"italic\" \n fill=\"var(--symbol-stroke, #000000)\"\n >F</text>\n </g>\n `,\n metadata: {\n description: 'PSR F instrument symbol - Flow indicator',\n tags: ['instrument', 'psr', 'flow', 'indicator'],\n },\n },\n \n 'psr-p': {\n id: 'psr-p',\n name: 'PSR P',\n category: 'instrument',\n viewBox: { x: 0, y: 0, width: 28.5, height: 28.5 },\n ports: [\n { id: 'top', type: 'signal', x: 14.25, y: 7.125 },\n { id: 'right', type: 'signal', x: 21.375, y: 14.25 },\n { id: 'bottom', type: 'signal', x: 14.25, y: 21.375 },\n { id: 'left', type: 'signal', x: 7.125, y: 14.25 },\n ],\n svgContent: `\n <g transform=\"scale(0.25) translate(28.5, 28.5)\">\n <!-- Circle background -->\n <ellipse \n cx=\"28.5\" \n cy=\"28.5\" \n rx=\"28.5\" \n ry=\"28.5\" \n stroke=\"var(--symbol-stroke, #000000)\" \n stroke-width=\"1\" \n fill=\"var(--symbol-fill, #ffffff)\"\n />\n \n <!-- Letter P -->\n <text \n x=\"28.5\" \n y=\"38\" \n text-anchor=\"middle\" \n font-family=\"'Times New Roman', Georgia, serif\" \n font-size=\"34\" \n font-weight=\"normal\" \n font-style=\"italic\" \n fill=\"var(--symbol-stroke, #000000)\"\n >P</text>\n </g>\n `,\n metadata: {\n description: 'PSR P instrument symbol - Pressure indicator',\n tags: ['instrument', 'psr', 'pressure', 'indicator'],\n },\n },\n};\n\n/**\n * Custom symbol registry for user-defined symbols\n */\nconst customSymbols: Record<string, SymbolDefinition> = {};\n\n/**\n * Register a custom symbol definition\n * @param symbol - The symbol definition to register\n * @example\n * registerSymbol({\n * id: 'my-custom-valve',\n * name: 'My Custom Valve',\n * category: 'valve',\n * viewBox: { x: 0, y: 0, width: 100, height: 100 },\n * ports: [...],\n * svgContent: '...',\n * });\n */\nexport function registerSymbol(symbol: SymbolDefinition): void {\n customSymbols[symbol.id] = symbol;\n}\n\n/**\n * Register multiple custom symbols at once\n * @param symbols - Array of symbol definitions or a record of symbols\n */\nexport function registerSymbols(symbols: SymbolDefinition[] | Record<string, SymbolDefinition>): void {\n if (Array.isArray(symbols)) {\n symbols.forEach((symbol) => registerSymbol(symbol));\n } else {\n Object.values(symbols).forEach((symbol) => registerSymbol(symbol));\n }\n}\n\n/**\n * Get symbol definition by ID (checks both mock library and custom symbols)\n */\nexport function getSymbolDefinition(symbolId: string): SymbolDefinition | undefined {\n return customSymbols[symbolId] ?? mockSymbolLibrary[symbolId];\n}\n\n/**\n * Get all available symbol IDs (includes both mock library and custom symbols)\n */\nexport function getAvailableSymbols(): string[] {\n return [...Object.keys(customSymbols), ...Object.keys(mockSymbolLibrary)];\n}\n","import type { NodeInstance } from '../schema';\n\n/**\n * Appearance schema — the reusable contract for per-element editable properties.\n *\n * A placeable element declares its editable appearance fields as data. The\n * properties panel (NodeConfigPanel) renders any schema generically, and the\n * renderer (NodeRenderer) reads the same defaults. Add a new element by adding\n * one schema entry — no panel code changes. This is the pattern the\n * \"add a placeable element\" skill generates.\n */\n\nexport type AppearanceFieldKind = 'select' | 'boolean' | 'number' | 'text' | 'tag';\n\n/**\n * Where a field's value is read from and written to on the NodeInstance.\n * - 'metadata' (default): the untyped node.metadata bag, keyed by field.key\n * - 'visible': the node.visible flag\n * - 'scale': node.transform.scale (via valueMap for select options)\n */\nexport type AppearanceTarget = 'metadata' | 'visible' | 'scale';\n\nexport interface AppearanceField {\n /** metadata key, or the driver for the special 'visible'/'scale' targets */\n key: string;\n kind: AppearanceFieldKind;\n /** Human label for the panel; defaults to key when absent */\n label?: string;\n /** Options for kind 'select' */\n options?: string[];\n /** Where the value is stored; defaults to 'metadata' */\n target?: AppearanceTarget;\n /** Default value when the node has no value set */\n default?: string | number | boolean;\n /** For target 'scale': maps a select option to a numeric scale factor */\n valueMap?: Record<string, number>;\n /** Optional help text shown under the control in the panel */\n help?: string;\n}\n\nexport type AppearanceValue = string | number | boolean;\n\n/** Named color presets for the boolean indicator (name → hex). */\nexport const COLOR_PRESETS: Record<string, string> = {\n green: '#22c55e',\n red: '#ef4444',\n amber: '#f59e0b',\n blue: '#3b82f6',\n grey: '#64748b',\n};\n\n/** Resolve a color preset name to a hex value, falling back to grey. */\nexport function resolvePresetColor(name: string | undefined): string {\n return (name && COLOR_PRESETS[name]) || COLOR_PRESETS.grey;\n}\n\nconst COLOR_OPTIONS = Object.keys(COLOR_PRESETS);\n\n/** Appearance schema for the Status LED symbol. */\nexport const STATUS_LED_APPEARANCE: AppearanceField[] = [\n {\n key: 'shape',\n kind: 'select',\n label: 'Shape',\n options: ['circle', 'square', 'diamond'],\n default: 'circle',\n },\n {\n key: 'size',\n kind: 'select',\n label: 'Size',\n target: 'scale',\n options: ['small', 'medium', 'large'],\n default: 'medium',\n valueMap: { small: 0.75, medium: 1, large: 1.5 },\n },\n { key: 'led', kind: 'boolean', label: 'Bezel ring', default: true },\n { key: 'glow', kind: 'boolean', label: 'Diffusion (glow)', default: false },\n {\n key: 'animation',\n kind: 'select',\n label: 'Animation',\n options: ['none', 'pulse', 'blink'],\n default: 'none',\n },\n {\n key: 'labelPosition',\n kind: 'select',\n label: 'Label position',\n options: ['bottom', 'top', 'left', 'right'],\n default: 'bottom',\n },\n // Color binding: a named signal drives the on/off color.\n {\n key: 'colorSource',\n kind: 'tag',\n label: 'Color source (tag)',\n default: '',\n help: 'Signal/tag that drives color. True (non-zero) shows On color; false shows Off color. Blank = unbound (grey).',\n },\n {\n key: 'onColor',\n kind: 'select',\n label: 'On color (true)',\n options: COLOR_OPTIONS,\n default: 'green',\n help: 'Color when the Color source is true.',\n },\n {\n key: 'offColor',\n kind: 'select',\n label: 'Off color (false)',\n options: COLOR_OPTIONS,\n default: 'red',\n help: 'Color when the Color source is false.',\n },\n // Visibility binding: an independent named signal drives show/hide.\n {\n key: 'visibleSource',\n kind: 'tag',\n label: 'Visibility source (tag)',\n default: '',\n help: 'Signal/tag that drives visibility. True shows the LED, false hides it. Blank = always visible (use the Visible checkbox).',\n },\n {\n key: 'visible',\n kind: 'boolean',\n label: 'Visible',\n target: 'visible',\n default: true,\n help: 'Static show/hide, used only when no Visibility source is set.',\n },\n];\n\n/** Registry of symbol id → appearance schema. Add one entry per placeable element. */\nconst APPEARANCE_REGISTRY: Record<string, AppearanceField[]> = {\n 'status-indicator': STATUS_LED_APPEARANCE,\n};\n\n/** Return the editable appearance fields for a symbol, or [] if it declares none. */\nexport function getAppearanceSchema(symbolId: string): AppearanceField[] {\n return APPEARANCE_REGISTRY[symbolId] ?? [];\n}\n\n/**\n * Default values for the 'metadata'-targeted fields of a symbol, keyed by field key.\n * The renderer merges these under the node's own metadata, so a freshly dropped\n * element looks correct before any editing.\n */\nexport function getAppearanceDefaults(symbolId: string): Record<string, AppearanceValue> {\n const out: Record<string, AppearanceValue> = {};\n for (const field of getAppearanceSchema(symbolId)) {\n if ((field.target ?? 'metadata') === 'metadata' && field.default !== undefined) {\n out[field.key] = field.default;\n }\n }\n return out;\n}\n\n/** Read the current value of a field from a node, applying the field default. */\nexport function readAppearanceValue(node: NodeInstance, field: AppearanceField): AppearanceValue {\n if (field.target === 'visible') {\n return node.visible ?? (field.default as boolean | undefined) ?? true;\n }\n if (field.target === 'scale') {\n const scale = node.transform.scale ?? 1;\n if (field.valueMap) {\n let bestOption = field.options?.[0] ?? String(scale);\n let bestDiff = Infinity;\n for (const [option, value] of Object.entries(field.valueMap)) {\n const diff = Math.abs(value - scale);\n if (diff < bestDiff) {\n bestDiff = diff;\n bestOption = option;\n }\n }\n return bestOption;\n }\n return scale;\n }\n const value = node.metadata?.[field.key];\n return (value as AppearanceValue | undefined) ?? (field.default as AppearanceValue);\n}\n\n/** Produce the NodeInstance update for a new field value. */\nexport function writeAppearanceValue(\n node: NodeInstance,\n field: AppearanceField,\n value: AppearanceValue\n): Partial<NodeInstance> {\n if (field.target === 'visible') {\n return { visible: Boolean(value) };\n }\n if (field.target === 'scale') {\n const scale = field.valueMap ? (field.valueMap[String(value)] ?? 1) : Number(value);\n return { transform: { ...node.transform, scale } };\n }\n return { metadata: { ...node.metadata, [field.key]: value } };\n}\n","/**\n * Registry of badge glyphs for the `NodeBadge` corner slot (#33).\n *\n * Mirrors `diagram/symbols/mockSymbolLibrary`'s registration pattern: a\n * built-in set plus a consumer-registered set, looked up by id. A consumer\n * calls `registerBadgeGlyph` from its own app code to add a glyph the\n * library does not ship - the same escape hatch `registerSymbol` already\n * gives P&ID symbols. This is deliberately NOT a string resolved only\n * inside the library (the mistake `Overlay.content.component` made, per\n * #33): the consumer supplies the real markup.\n */\nexport interface BadgeGlyphDefinition {\n /** Id a `NodeBadge.glyph` references. */\n id: string;\n /**\n * Raw SVG markup, drawn centered in a 24x24 local box (0,0 to 24,24).\n * Use `stroke=\"currentColor\"` / `fill=\"currentColor\"` so the glyph picks\n * up `NodeBadge.color` - the glyph carries no color of its own, only shape.\n */\n svgContent: string;\n}\n\n/** A circle with a slash: the prohibition sign the TFFi application needs first. */\nconst PROHIBIT_GLYPH: BadgeGlyphDefinition = {\n id: 'prohibit',\n svgContent:\n '<circle cx=\"12\" cy=\"12\" r=\"9\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\"/>' +\n '<line x1=\"5.6\" y1=\"18.4\" x2=\"18.4\" y2=\"5.6\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\"/>',\n};\n\nconst builtInBadgeGlyphs: Record<string, BadgeGlyphDefinition> = {\n [PROHIBIT_GLYPH.id]: PROHIBIT_GLYPH,\n};\n\nconst customBadgeGlyphs: Record<string, BadgeGlyphDefinition> = {};\n\n/** Register a single custom badge glyph, or replace a built-in one by reusing its id. */\nexport function registerBadgeGlyph(glyph: BadgeGlyphDefinition): void {\n customBadgeGlyphs[glyph.id] = glyph;\n}\n\n/** Register multiple custom badge glyphs at once. */\nexport function registerBadgeGlyphs(\n glyphs: BadgeGlyphDefinition[] | Record<string, BadgeGlyphDefinition>\n): void {\n if (Array.isArray(glyphs)) {\n glyphs.forEach((glyph) => registerBadgeGlyph(glyph));\n } else {\n Object.values(glyphs).forEach((glyph) => registerBadgeGlyph(glyph));\n }\n}\n\n/** Get a badge glyph definition by id (checks custom glyphs before built-ins). */\nexport function getBadgeGlyph(glyphId: string): BadgeGlyphDefinition | undefined {\n return customBadgeGlyphs[glyphId] ?? builtInBadgeGlyphs[glyphId];\n}\n\n/** List every registered badge glyph id (built-in plus custom). */\nexport function getAvailableBadgeGlyphs(): string[] {\n return [...Object.keys(customBadgeGlyphs), ...Object.keys(builtInBadgeGlyphs)];\n}\n","import React from 'react';\nimport type { PipeEdge, PipeStyle } from '../../diagram/schema';\n\nexport interface PipeRendererProps {\n /** Array of pipe edges to render */\n pipes: PipeEdge[];\n /** IDs of selected pipes */\n selectedPipeIds?: Set<string>;\n /** Callback when a pipe is clicked */\n onPipeClick?: (pipeId: string, event: React.MouseEvent) => void;\n /** Enable waypoint editing mode */\n enableWaypointEditing?: boolean;\n /** Callback when waypoint drag starts */\n onWaypointDragStart?: (\n pipeId: string,\n pointIndex: number,\n event: React.MouseEvent | React.TouchEvent\n ) => void;\n /** Callback when waypoint is clicked (for selection) */\n onWaypointClick?: (pipeId: string, pointIndex: number) => void;\n /** Selected waypoint pipe ID */\n selectedWaypointPipeId?: string | null;\n /** Selected waypoint point index */\n selectedWaypointIndex?: number | null;\n /** ID of pipe being edited */\n editingPipeId?: string | null;\n /** Index of waypoint being dragged */\n draggingWaypointIndex?: number | null;\n /** Offset for dragged waypoint */\n waypointDragOffset?: { x: number; y: number };\n /** Callback when pipe segment is clicked (to add waypoint) */\n onPipeSegmentClick?: (\n pipeId: string,\n position: { x: number; y: number },\n segmentIndex: number\n ) => void;\n}\n\n/**\n * PipeRenderer - Renders pipe connections between nodes with selection support\n *\n * Uses SVG polyline elements to render pipes with route points\n * Supports click selection with visual feedback\n *\n * Memoized (renderer-OOM fix): receives no telemetry, so on a telemetry-driven\n * canvas re-render every prop is unchanged and the pipe layer bails out.\n * Callers must pass identity-stable callbacks (PIDCanvas useCallback-wraps\n * its pipe handlers for this).\n */\nfunction PipeRendererImpl({\n pipes,\n selectedPipeIds,\n onPipeClick,\n enableWaypointEditing = false,\n onWaypointDragStart,\n onWaypointClick,\n selectedWaypointPipeId,\n selectedWaypointIndex,\n editingPipeId,\n draggingWaypointIndex,\n waypointDragOffset,\n onPipeSegmentClick,\n}: PipeRendererProps): React.ReactElement {\n return (\n <g id=\"layer-pipes\">\n {pipes\n .filter((pipe) => pipe.visible !== false) // Filter hidden pipes\n .map((pipe) => {\n const isSelected = selectedPipeIds?.has(pipe.id) ?? false;\n const isEditing = enableWaypointEditing && editingPipeId === pipe.id;\n const showEditingControls = enableWaypointEditing && isSelected;\n\n // Apply waypoint drag offset if dragging\n const displayPoints = pipe.routePoints.map((point, idx) => {\n if (isEditing && draggingWaypointIndex === idx && waypointDragOffset) {\n return {\n x: point.x + waypointDragOffset.x,\n y: point.y + waypointDragOffset.y,\n };\n }\n return point;\n });\n\n // Convert route points to polyline points string\n const pointsString = displayPoints.map((point) => `${point.x},${point.y}`).join(' ');\n\n // Apply pipe style with defaults\n const pipeStyle: Partial<PipeStyle> = pipe.style || {};\n const stroke = isSelected ? '#3b82f6' : pipeStyle.stroke || '#94a1b8';\n const strokeWidth = isSelected\n ? (pipeStyle.strokeWidth || 3) + 2\n : pipeStyle.strokeWidth || 3;\n const strokeDasharray = pipeStyle.strokeDasharray;\n const opacity = pipeStyle.opacity !== undefined ? pipeStyle.opacity : 1;\n\n return (\n <g\n key={pipe.id}\n data-pipe-id={pipe.id}\n data-selected={isSelected}\n onClick={(e) => {\n e.stopPropagation();\n onPipeClick?.(pipe.id, e);\n }}\n style={{\n cursor: onPipeClick ? 'pointer' : 'default',\n }}\n >\n {/* Only render pipe lines when NOT in waypoint-only mode */}\n {!enableWaypointEditing && (\n <>\n {/* Selection indicator - wider transparent clickable area */}\n {onPipeClick && (\n <polyline\n points={pointsString}\n fill=\"none\"\n stroke=\"transparent\"\n strokeWidth={strokeWidth + 8}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n pointerEvents=\"stroke\"\n />\n )}\n\n {/* Main pipe line */}\n <polyline\n points={pointsString}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n strokeDasharray={strokeDasharray}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n opacity={opacity}\n data-from={pipe.fromNodeId}\n data-to={pipe.toNodeId}\n pointerEvents={onPipeClick ? 'none' : 'auto'}\n />\n </>\n )}\n\n {/* Connection points indicators */}\n {displayPoints.map((point, idx) => {\n // Show all waypoints including endpoints\n const isEndpoint = idx === 0 || idx === displayPoints.length - 1;\n const isSelectedWaypoint =\n selectedWaypointPipeId === pipe.id && selectedWaypointIndex === idx;\n\n const isDragging = isEditing && draggingWaypointIndex === idx;\n const waypointSize = isSelectedWaypoint\n ? 8\n : isDragging\n ? 8\n : showEditingControls\n ? 6\n : isSelected\n ? 3\n : 2;\n const waypointOpacity = isSelectedWaypoint\n ? 1\n : isDragging\n ? 1\n : showEditingControls\n ? 0.9\n : isSelected\n ? 0.8\n : 0.5;\n\n // Color priority: selected > dragging > endpoint > default\n const waypointColor = isSelectedWaypoint\n ? '#3b82f6' // Blue for selected\n : isEndpoint && showEditingControls\n ? '#f59e0b' // Amber for endpoints\n : isDragging\n ? '#10b981' // Green for dragging\n : stroke; // Default pipe color\n\n return (\n <circle\n key={`${pipe.id}-point-${idx}`}\n cx={point.x}\n cy={point.y}\n r={waypointSize}\n fill={waypointColor}\n stroke={showEditingControls ? '#ffffff' : 'none'}\n strokeWidth={showEditingControls ? 2 : 0}\n opacity={waypointOpacity}\n className=\"pipe-route-point\"\n style={{\n cursor: showEditingControls ? (isDragging ? 'grabbing' : 'grab') : 'default',\n }}\n pointerEvents={showEditingControls ? 'all' : 'none'}\n onClick={(e) => {\n if (showEditingControls) {\n e.stopPropagation();\n onWaypointClick?.(pipe.id, idx);\n }\n }}\n onMouseDown={(e) => {\n if (showEditingControls) {\n e.stopPropagation();\n onWaypointDragStart?.(pipe.id, idx, e);\n }\n }}\n onTouchStart={(e) => {\n if (showEditingControls) {\n e.stopPropagation();\n onWaypointDragStart?.(pipe.id, idx, e);\n }\n }}\n />\n );\n })}\n\n {/* Pipe segment click areas (for adding waypoints) */}\n {showEditingControls && onPipeSegmentClick && displayPoints.length >= 2 && (\n <>\n {displayPoints.slice(0, -1).map((point, idx) => {\n const nextPoint = displayPoints[idx + 1];\n const midX = (point.x + nextPoint.x) / 2;\n const midY = (point.y + nextPoint.y) / 2;\n\n return (\n <g key={`${pipe.id}-segment-${idx}`}>\n {/* Larger clickable area */}\n <circle\n cx={midX}\n cy={midY}\n r={8}\n fill=\"transparent\"\n className=\"pipe-segment-add-area\"\n style={{ cursor: 'crosshair' }}\n pointerEvents=\"all\"\n onClick={(e) => {\n e.stopPropagation();\n onPipeSegmentClick?.(pipe.id, { x: midX, y: midY }, idx);\n }}\n />\n {/* Visible indicator */}\n <circle\n cx={midX}\n cy={midY}\n r={4}\n fill=\"#3b82f6\"\n stroke=\"#ffffff\"\n strokeWidth={2}\n opacity={0.6}\n className=\"pipe-segment-add\"\n pointerEvents=\"none\"\n />\n </g>\n );\n })}\n </>\n )}\n\n {/* Pipe label (optional) */}\n {pipe.showLabel !== false && pipe.label && pipe.routePoints.length >= 2 && (\n <text\n x={\n pipe.routePoints[Math.floor(pipe.routePoints.length / 2)].x +\n (pipe.labelOffset?.x ?? 0)\n }\n y={\n pipe.routePoints[Math.floor(pipe.routePoints.length / 2)].y -\n 8 +\n (pipe.labelOffset?.y ?? 0)\n }\n textAnchor=\"middle\"\n fontSize=\"10\"\n fontWeight={isSelected ? '600' : '500'}\n fill={stroke}\n opacity={opacity * 0.8}\n pointerEvents=\"none\"\n >\n {pipe.label}\n </text>\n )}\n </g>\n );\n })}\n </g>\n );\n}\n\n/** Memo boundary - see PipeRendererImpl doc comment (renderer-OOM fix). */\nexport const PipeRenderer = React.memo(PipeRendererImpl);\n","import React from 'react';\nimport type { TelemetryBinding, TelemetryStatus } from '../../diagram/schema/TelemetryBinding';\nimport { resolveStatusColor, normalizeDeviceStatus, FORCED_MARK_COLOR } from '../../theme/statusColors';\nimport type { StatusColorMap } from '../../theme/statusColors';\nimport { UnknownStatusGlyph } from '../../theme/unknownStatusGlyph';\n\n/**\n * The status dot's pulse used to be a SMIL `<animate repeatCount=\"indefinite\">`.\n * SMIL animations with an indefinite repeat are retained by Blink's SMIL\n * timeline (as native \"traced handles\") when their element is removed from the\n * DOM mid-animation — the classic Chromium detached-node leak. DataOverlay\n * mounts/unmounts this dot every time a node's status crosses a threshold\n * (i.e. up to once per telemetry tick in a live system), so the orphaned\n * animated <circle>s accumulate monotonically. A CSS keyframe animation is\n * torn down by Blink on element removal, so we drive the pulse from CSS\n * instead. The keyframe is injected once into <head>, not per instance.\n */\nconst PULSE_KEYFRAME_ID = 'pid-data-overlay-pulse-keyframes';\nconst PULSE_ANIMATION = 'pid-data-overlay-pulse 1.5s ease-in-out infinite';\n\nfunction ensurePulseKeyframes(): void {\n if (typeof document === 'undefined') return; // SSR guard\n if (document.getElementById(PULSE_KEYFRAME_ID)) return; // idempotent\n const style = document.createElement('style');\n style.id = PULSE_KEYFRAME_ID;\n style.textContent =\n '@keyframes pid-data-overlay-pulse{0%{opacity:.9}50%{opacity:.3}100%{opacity:.9}}';\n document.head.appendChild(style);\n}\n\nfunction renderStatusDot(\n cx: number,\n cy: number,\n scale: number,\n fill: string,\n status: TelemetryStatus\n): React.ReactElement {\n // ISA-18.2 reserves blinking for an unacknowledged alarm (KTD5). An\n // `unknown` value is not an alarm, so its dot stays solid: no pulse.\n const isUnknown = normalizeDeviceStatus(status) === 'unknown';\n if (!isUnknown) ensurePulseKeyframes();\n return (\n <circle\n className=\"pid-data-overlay-status-dot\"\n cx={cx + 35 * scale}\n cy={cy - 18 * scale}\n r={3 * scale}\n fill={fill}\n style={isUnknown ? undefined : { animation: PULSE_ANIMATION }}\n />\n );\n}\n\nexport interface DataOverlayProps {\n /** Node ID this overlay is for */\n nodeId: string;\n /** X position in SVG coordinates */\n x: number;\n /** Y position in SVG coordinates */\n y: number;\n /** Telemetry binding data */\n telemetry: TelemetryBinding;\n /** Scale factor for display size (default: 1.0) */\n scale?: number;\n /** Custom status-to-color map (overrides ISA-101 defaults) */\n statusColorMap?: StatusColorMap;\n}\n\n/**\n * DataOverlay - Renders live telemetry values above P&ID nodes\n *\n * Shows:\n * - Current value with unit\n * - Status-based color coding\n * - Optional label\n *\n * Memoized (renderer-OOM fix): a live P&ID re-renders the canvas ~1Hz, and\n * every overlay re-rendering every tick - text re-layout, drop-shadow rect,\n * sparkline path recompute - forces recurring large-area SVG rasterization.\n * Under kiosk Chromium's --disable-gpu software compositor that leaks\n * /dev/shm tile buffers until the renderer OOMs. All props except `telemetry`\n * and `statusColorMap` are primitives derived from node geometry, so when the\n * host preserves binding identity for unchanged entries, an unchanged overlay\n * bails out of the re-render entirely.\n */\nfunction DataOverlayImpl({\n nodeId,\n x,\n y,\n telemetry,\n scale = 1.0,\n statusColorMap,\n}: DataOverlayProps): React.ReactElement {\n const { value, display, history, scaling, forced } = telemetry;\n const isForced = forced?.active === true;\n const hasRealValue = isForced && forced?.realValue !== undefined;\n const showValue = display?.showValue ?? true;\n const showStatus = display?.showStatus ?? true;\n const showSparkline = display?.showSparkline ?? false;\n const label = display?.label;\n // Scaling metadata (#1663) is the authoritative source when present:\n // decimals -> precision, units -> unit label, eu range -> sparkline axis.\n // Falls back to the static display.precision / value.unit when absent.\n const precision = scaling?.decimals ?? display?.precision;\n const unit = scaling?.units ?? value.unit;\n const sparklineColor = display?.sparklineColor;\n const textColor = display?.textColor;\n const backgroundColor = display?.backgroundColor;\n const offsetX = display?.offsetX ?? 0;\n const offsetY = display?.offsetY ?? 0;\n\n // Calculate sparkline path if history is provided. Memoized so a change to\n // value/status alone does not redo the per-point path math; keyed on the\n // history ARRAY identity, which the host only replaces when it samples a new\n // point (renderer-OOM fix). Placed before the showValue early-return to keep\n // hook order unconditional.\n const sparklinePath = React.useMemo(() => {\n if (!showSparkline || !history || history.length < 2) return '';\n\n const width = 70 * scale;\n const height = 20 * scale;\n const padding = 5 * scale;\n const effectiveWidth = width - padding * 2;\n const effectiveHeight = height - padding * 2;\n\n // Plot against the EU range when scaling metadata provides one (a fixed,\n // meaningful trend axis), else fall back to the history min/max (#1663).\n const hasEuRange =\n scaling?.euLow !== undefined &&\n scaling?.euHigh !== undefined &&\n scaling.euHigh !== scaling.euLow;\n const min = hasEuRange ? (scaling.euLow as number) : Math.min(...history);\n const max = hasEuRange ? (scaling.euHigh as number) : Math.max(...history);\n const range = max - min || 1; // Avoid division by zero\n\n const points = history.map((val, i) => {\n const xPos = (i / (history.length - 1)) * effectiveWidth + padding;\n const yPos = height - padding - ((val - min) / range) * effectiveHeight;\n return `${xPos.toFixed(2)},${yPos.toFixed(2)}`;\n });\n\n return `M ${points.join(' L ')}`;\n }, [showSparkline, history, scaling, scale]);\n\n if (!showValue) {\n return <></>;\n }\n\n // Format value based on type and precision\n const formatValue = (val: number | string | boolean): string => {\n if (typeof val === 'number') {\n return precision !== undefined ? val.toFixed(precision) : val.toString();\n }\n if (typeof val === 'boolean') {\n return val ? 'ON' : 'OFF';\n }\n return val.toString();\n };\n\n // Resolve status colors: per-binding override > statusColorMap > ISA-101 defaults\n const resolvedEntry = resolveStatusColor(value.status, statusColorMap);\n const customColor = telemetry.statusColors?.[value.status];\n const statusColors = customColor\n ? { bg: customColor, text: '#ffffff', border: customColor }\n : { bg: resolvedEntry.bg, text: resolvedEntry.text, border: resolvedEntry.border };\n\n // Apply explicit backgroundColor override if provided (and not 'status')\n const colors =\n backgroundColor && backgroundColor !== 'status'\n ? { bg: backgroundColor, text: textColor || '#ffffff', border: backgroundColor }\n : statusColors;\n\n // Override text color if explicitly set\n const finalTextColor = textColor || colors.text;\n\n const formattedValue = formatValue(value.value);\n const displayText = unit ? `${formattedValue} ${unit}` : formattedValue;\n const realDisplayText = hasRealValue\n ? unit\n ? `REAL ${formatValue(forced!.realValue as number | string | boolean)} ${unit}`\n : `REAL ${formatValue(forced!.realValue as number | string | boolean)}`\n : undefined;\n\n // A forced value is not a health status (#3607): the border marks it\n // independently of the status-driven fill, so a forced-but-normal reading\n // never reads as an alarm and an alarming forced reading still shows its\n // status color.\n const borderColor = isForced ? FORCED_MARK_COLOR.border : colors.border;\n const borderWidth = (isForced ? 2 : 1) * scale;\n\n const hasSparkline = showSparkline && sparklinePath;\n // The FORCED mark is a corner badge (below) and costs no extra row. Only\n // the REAL value line, when present, needs one.\n const extraMarkRows = realDisplayText ? 1 : 0;\n const overlayHeight =\n (label ? (hasSparkline ? 55 : 30) : hasSparkline ? 47 : 22) * scale + extraMarkRows * 9 * scale;\n const overlayWidth = 80 * scale;\n\n // Decide \"is this value untrusted?\" once, through the shared normalization\n // path. A local string compare here could disagree with the colour the\n // badge is painted with once an alias changes.\n const isUnknownValue = normalizeDeviceStatus(value.status) === 'unknown';\n\n // Apply position offsets\n const displayX = x + offsetX;\n const displayY = y + offsetY;\n\n return (\n <g data-overlay-node={nodeId} data-status={value.status} data-forced={isForced}>\n {/* Background rect */}\n <rect\n x={displayX - overlayWidth / 2}\n y={displayY - 25 * scale}\n width={overlayWidth}\n height={overlayHeight}\n rx={4 * scale}\n fill={colors.bg}\n stroke={borderColor}\n strokeWidth={borderWidth}\n opacity=\"0.95\"\n filter=\"drop-shadow(0 2px 4px rgba(0,0,0,0.2))\"\n />\n\n {/* Label (optional) */}\n {label && (\n <text\n x={displayX}\n y={displayY - 15 * scale}\n textAnchor=\"middle\"\n fontSize={8 * scale}\n fontWeight=\"600\"\n fill={finalTextColor}\n opacity=\"0.9\"\n >\n {label}\n </text>\n )}\n\n {/* Value text */}\n <text\n x={displayX}\n y={label ? displayY - 3 * scale : displayY - 9 * scale}\n textAnchor=\"middle\"\n fontSize={11 * scale}\n fontWeight=\"700\"\n fill={finalTextColor}\n letterSpacing=\"0.02em\"\n >\n {displayText}\n </text>\n\n {/* Status indicator dot (optional).\n Pulse is driven by a CSS keyframe (not SMIL) so Blink releases the\n element when React unmounts it — see ensurePulseKeyframes above. */}\n {showStatus &&\n value.status !== 'normal' &&\n renderStatusDot(displayX, displayY, scale, finalTextColor, value.status)}\n\n {/* Unknown-status glyph (R9, R10, R11): a non-colour marker for a value\n the backend does not trust. One shared module draws it (KTD10), so\n the marker cannot drift from StatusIndicator or DeviceControlPanel.\n Placed in user units, scaled like every other element in this file.\n Gated on showStatus for the same reason the dot is: a caller that\n turned the status channel off does not expect either marker.\n\n Placed on the LEFT corner. The forced-value badge (#26) occupies the\n right corner at the same height, and a value can be both forced and\n untrusted at once. The two markers answer different questions, so\n they must stay separately readable rather than overlap. */}\n {showStatus && isUnknownValue && (\n <UnknownStatusGlyph\n x={displayX - overlayWidth / 2 + 2 * scale}\n y={displayY - 25 * scale - 6 * scale}\n width={12 * scale}\n height={12 * scale}\n />\n )}\n\n {/* Quality indicator (if provided and < 100) */}\n {value.quality !== undefined && value.quality < 100 && (\n <text\n x={displayX}\n y={displayY + 5 * scale}\n textAnchor=\"middle\"\n fontSize={7 * scale}\n fontWeight=\"500\"\n fill={finalTextColor}\n opacity=\"0.7\"\n >\n Q: {value.quality}%\n </text>\n )}\n\n {/* Forced-value marking (#3607, R19). A corner glyph badge, not a\n spelled-out word, so it reads at a glance and costs no extra row.\n A filled circle, not colored text, so it stays legible against\n every status background, including alarm red. Sized to remain\n readable on a 15\" panel-mount screen from a meter away, not\n shrunk purely to save space. */}\n {isForced && (\n <g\n transform={`translate(${displayX + overlayWidth / 2}, ${displayY - 25 * scale})`}\n aria-label=\"Forced\"\n >\n <circle\n r={7 * scale}\n fill={FORCED_MARK_COLOR.bg}\n stroke={FORCED_MARK_COLOR.border}\n strokeWidth={1.5 * scale}\n />\n <text\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n fontSize={8 * scale}\n fontWeight=\"700\"\n fill={FORCED_MARK_COLOR.text}\n >\n F\n </text>\n </g>\n )}\n\n {/* Real (unforced) value, shown beside the forced one (R19). Absent\n until the backend publishes it (alphinity-tffi unit U3b); the\n corner badge above still renders on its own until then. */}\n {realDisplayText && (\n <text\n x={displayX}\n y={displayY + (value.quality !== undefined && value.quality < 100 ? 14 : 5) * scale}\n textAnchor=\"middle\"\n fontSize={7 * scale}\n fontWeight=\"500\"\n fill={finalTextColor}\n opacity=\"0.85\"\n >\n {realDisplayText}\n </text>\n )}\n\n {/* Sparkline trend (if history provided) */}\n {hasSparkline && (\n <g transform={`translate(${displayX - 35 * scale}, ${displayY + (label ? 5 : 0) * scale})`}>\n <path\n d={sparklinePath}\n fill=\"none\"\n stroke={sparklineColor || finalTextColor}\n strokeWidth={1.5 * scale}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n opacity=\"0.9\"\n />\n </g>\n )}\n </g>\n );\n}\n\n/**\n * Memo boundary: bail out when nothing this overlay renders changed. Shallow\n * prop compare is exactly right here - `telemetry` (the binding object) and\n * `statusColorMap` are compared by identity, everything else is a primitive.\n * The host contract (documented on DataOverlayImpl) is that binding identity\n * only changes when displayed content changed.\n */\nexport const DataOverlay = React.memo(DataOverlayImpl);\n","import React, { useState, useEffect } from 'react';\nimport type { NodeInstance, Point } from '../../diagram/schema';\n\nexport interface PositionPanelProps {\n /** The selected node to display/edit position for */\n selectedNode?: NodeInstance | null;\n /** The selected waypoint to display/edit position for */\n selectedWaypoint?: { pipeId: string; pointIndex: number; position: Point } | null;\n /** Callback when node position is changed */\n onPositionChange?: (nodeId: string, x: number, y: number) => void;\n /** Callback when waypoint position is changed */\n onWaypointPositionChange?: (pipeId: string, pointIndex: number, x: number, y: number) => void;\n /** Position of the panel (default: top-right) */\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n}\n\n/**\n * PositionPanel - Displays and allows editing of selected node or waypoint position\n */\nexport function PositionPanel({\n selectedNode,\n selectedWaypoint,\n onPositionChange,\n onWaypointPositionChange,\n position = 'top-right',\n}: PositionPanelProps): React.ReactElement | null {\n const [x, setX] = useState<string>('');\n const [y, setY] = useState<string>('');\n\n // Update inputs when selected node or waypoint changes\n useEffect(() => {\n if (selectedNode) {\n setX(selectedNode.transform.x.toFixed(2));\n setY(selectedNode.transform.y.toFixed(2));\n } else if (selectedWaypoint) {\n setX(selectedWaypoint.position.x.toFixed(2));\n setY(selectedWaypoint.position.y.toFixed(2));\n }\n }, [selectedNode, selectedWaypoint]);\n\n if (!selectedNode && !selectedWaypoint) {\n return null;\n }\n\n const handleXChange = (value: string) => {\n setX(value);\n const numValue = parseFloat(value);\n if (!isNaN(numValue)) {\n if (selectedNode && onPositionChange) {\n onPositionChange(selectedNode.id, numValue, selectedNode.transform.y);\n } else if (selectedWaypoint && onWaypointPositionChange) {\n onWaypointPositionChange(\n selectedWaypoint.pipeId,\n selectedWaypoint.pointIndex,\n numValue,\n selectedWaypoint.position.y\n );\n }\n }\n };\n\n const handleYChange = (value: string) => {\n setY(value);\n const numValue = parseFloat(value);\n if (!isNaN(numValue)) {\n if (selectedNode && onPositionChange) {\n onPositionChange(selectedNode.id, selectedNode.transform.x, numValue);\n } else if (selectedWaypoint && onWaypointPositionChange) {\n onWaypointPositionChange(\n selectedWaypoint.pipeId,\n selectedWaypoint.pointIndex,\n selectedWaypoint.position.x,\n numValue\n );\n }\n }\n };\n\n const handleBlur = () => {\n // Reset to actual position on blur (handles invalid input)\n if (selectedNode) {\n setX(selectedNode.transform.x.toFixed(2));\n setY(selectedNode.transform.y.toFixed(2));\n } else if (selectedWaypoint) {\n setX(selectedWaypoint.position.x.toFixed(2));\n setY(selectedWaypoint.position.y.toFixed(2));\n }\n };\n\n const positionStyles: Record<string, React.CSSProperties> = {\n 'top-left': { top: 10, left: 10 },\n 'top-right': { top: 10, right: 10 },\n 'bottom-left': { bottom: 10, left: 10 },\n 'bottom-right': { bottom: 10, right: 10 },\n };\n\n return (\n <div\n style={{\n position: 'absolute',\n ...positionStyles[position],\n backgroundColor: 'white',\n border: '1px solid #ccc',\n borderRadius: '4px',\n padding: '12px',\n boxShadow: '0 2px 8px rgba(0,0,0,0.1)',\n zIndex: 1000,\n minWidth: '180px',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n fontSize: '13px',\n }}\n >\n <div style={{ marginBottom: '8px', fontWeight: 600, color: '#333' }}>Position</div>\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <label style={{ width: '16px', color: '#666' }}>X:</label>\n <input\n type=\"number\"\n value={x}\n onChange={(e) => handleXChange(e.target.value)}\n onBlur={handleBlur}\n style={{\n flex: 1,\n padding: '4px 8px',\n border: '1px solid #ddd',\n borderRadius: '3px',\n fontSize: '13px',\n }}\n step=\"1\"\n />\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <label style={{ width: '16px', color: '#666' }}>Y:</label>\n <input\n type=\"number\"\n value={y}\n onChange={(e) => handleYChange(e.target.value)}\n onBlur={handleBlur}\n style={{\n flex: 1,\n padding: '4px 8px',\n border: '1px solid #ddd',\n borderRadius: '3px',\n fontSize: '13px',\n }}\n step=\"1\"\n />\n </div>\n </div>\n <div style={{ marginTop: '8px', fontSize: '11px', color: '#888' }}>\n Use arrow keys to nudge (±1px)\n </div>\n </div>\n );\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { ViewBox } from '../schema';\n\nexport interface UseViewBoxOptions {\n /** Initial viewBox configuration */\n initialViewBox: ViewBox;\n /** Enable pan interaction (default: true) */\n enablePan?: boolean;\n /** Enable zoom interaction (default: true) */\n enableZoom?: boolean;\n /** Minimum zoom level (smaller = more zoomed in, default: 0.1) */\n minZoom?: number;\n /** Maximum zoom level (larger = more zoomed out, default: 10) */\n maxZoom?: number;\n /** Zoom sensitivity (default: 0.001) */\n zoomSensitivity?: number;\n /** Allow left-click drag for panning (default: true for touch-friendly) */\n allowLeftClickPan?: boolean;\n}\n\nexport interface UseViewBoxResult {\n /** Current viewBox state */\n viewBox: ViewBox;\n /** Reset viewBox to initial state */\n resetViewBox: () => void;\n /** Zoom in (decrease viewBox size) */\n zoomIn: () => void;\n /** Zoom out (increase viewBox size) */\n zoomOut: () => void;\n /** Fit viewBox to content bounds with optional padding */\n fitToContent: (bounds: { minX: number; minY: number; maxX: number; maxY: number }, padding?: number) => void;\n /** Set viewBox directly */\n setViewBox: (viewBox: ViewBox) => void;\n /** Convert screen coordinates to world (SVG) coordinates */\n screenToWorld: (screenX: number, screenY: number) => { x: number; y: number };\n /** SVG element ref to attach event listeners */\n svgRef: React.RefObject<SVGSVGElement | null>;\n}\n\n/**\n * Custom hook for managing ViewBox state with pan and zoom interactions\n * \n * Pan controls:\n * - Left mouse button + drag (if allowLeftClickPan=true, default)\n * - Middle mouse button + drag (always)\n * - Space + left mouse button + drag (always)\n * - Touch drag (single finger)\n * \n * Zoom controls:\n * - Mouse wheel (zoom to cursor position)\n * - Pinch gesture (touch devices)\n * - Programmatic zoomIn/zoomOut functions\n */\nexport function useViewBox(options: UseViewBoxOptions): UseViewBoxResult {\n const {\n initialViewBox,\n enablePan = true,\n enableZoom = true,\n minZoom = 0.1,\n maxZoom = 10,\n zoomSensitivity = 0.001,\n allowLeftClickPan = true,\n } = options;\n\n const [viewBox, setViewBox] = useState<ViewBox>(initialViewBox);\n const svgRef = useRef<SVGSVGElement | null>(null);\n const isPanningRef = useRef(false);\n const lastMousePosRef = useRef({ x: 0, y: 0 });\n const spaceKeyDownRef = useRef(false);\n const lastTouchDistanceRef = useRef<number | null>(null);\n\n // Reset to initial viewBox\n const resetViewBox = useCallback(() => {\n setViewBox(initialViewBox);\n }, [initialViewBox]);\n\n // Zoom in (25% closer)\n const zoomIn = useCallback(() => {\n setViewBox((prev) => {\n const currentZoom = initialViewBox.width / prev.width;\n const newZoom = Math.min(1 / minZoom, currentZoom * 1.25);\n const scale = initialViewBox.width / newZoom;\n \n const centerX = prev.x + prev.width / 2;\n const centerY = prev.y + prev.height / 2;\n const newWidth = scale;\n const newHeight = (prev.height / prev.width) * scale;\n\n return {\n x: centerX - newWidth / 2,\n y: centerY - newHeight / 2,\n width: newWidth,\n height: newHeight,\n };\n });\n }, [initialViewBox, minZoom]);\n\n // Zoom out (25% farther)\n const zoomOut = useCallback(() => {\n setViewBox((prev) => {\n const currentZoom = initialViewBox.width / prev.width;\n const newZoom = Math.max(1 / maxZoom, currentZoom * 0.8);\n const scale = initialViewBox.width / newZoom;\n \n const centerX = prev.x + prev.width / 2;\n const centerY = prev.y + prev.height / 2;\n const newWidth = scale;\n const newHeight = (prev.height / prev.width) * scale;\n\n return {\n x: centerX - newWidth / 2,\n y: centerY - newHeight / 2,\n width: newWidth,\n height: newHeight,\n };\n });\n }, [initialViewBox, maxZoom]);\n\n // Fit to content bounds\n const fitToContent = useCallback((bounds: { minX: number; minY: number; maxX: number; maxY: number }, padding: number = 50) => {\n const contentWidth = bounds.maxX - bounds.minX;\n const contentHeight = bounds.maxY - bounds.minY;\n \n if (contentWidth === 0 || contentHeight === 0) {\n // No content or invalid bounds\n return;\n }\n\n // Add padding\n const paddedWidth = contentWidth + padding * 2;\n const paddedHeight = contentHeight + padding * 2;\n \n // Maintain aspect ratio of initial viewBox\n const aspectRatio = initialViewBox.width / initialViewBox.height;\n const contentAspectRatio = paddedWidth / paddedHeight;\n \n let newWidth: number;\n let newHeight: number;\n \n if (contentAspectRatio > aspectRatio) {\n // Content is wider - fit to width\n newWidth = paddedWidth;\n newHeight = paddedWidth / aspectRatio;\n } else {\n // Content is taller - fit to height\n newHeight = paddedHeight;\n newWidth = paddedHeight * aspectRatio;\n }\n \n const centerX = bounds.minX + contentWidth / 2;\n const centerY = bounds.minY + contentHeight / 2;\n \n setViewBox({\n x: centerX - newWidth / 2,\n y: centerY - newHeight / 2,\n width: newWidth,\n height: newHeight,\n });\n }, [initialViewBox]);\n\n // Handle mouse wheel zoom\n useEffect(() => {\n if (!enableZoom || !svgRef.current) return;\n\n const svg = svgRef.current;\n\n const handleWheel = (e: WheelEvent) => {\n e.preventDefault();\n\n // Get mouse position in SVG coordinate space\n const rect = svg.getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n\n setViewBox((prev) => {\n // Convert mouse position to viewBox coordinates\n const svgX = prev.x + (mouseX / rect.width) * prev.width;\n const svgY = prev.y + (mouseY / rect.height) * prev.height;\n\n // Calculate zoom factor\n const delta = -e.deltaY * zoomSensitivity;\n const zoom = Math.exp(delta);\n\n // Calculate new viewBox dimensions\n let newWidth = prev.width / zoom;\n let newHeight = prev.height / zoom;\n\n // Clamp zoom level\n const currentZoom = initialViewBox.width / prev.width;\n const newZoom = currentZoom * zoom;\n if (newZoom < minZoom || newZoom > 1 / minZoom) {\n return prev; // Don't zoom beyond limits\n }\n\n // Adjust viewBox position to zoom towards mouse cursor\n const newX = svgX - (mouseX / rect.width) * newWidth;\n const newY = svgY - (mouseY / rect.height) * newHeight;\n\n return {\n x: newX,\n y: newY,\n width: newWidth,\n height: newHeight,\n };\n });\n };\n\n svg.addEventListener('wheel', handleWheel, { passive: false });\n return () => svg.removeEventListener('wheel', handleWheel);\n }, [enableZoom, zoomSensitivity, minZoom, initialViewBox]);\n\n // Handle panning\n useEffect(() => {\n if (!enablePan || !svgRef.current) return;\n\n const svg = svgRef.current;\n\n const handleMouseDown = (e: MouseEvent) => {\n // Check if clicking on a draggable element (node, waypoint, pipe segment)\n const target = e.target as Element;\n const isDraggableNode = target.closest('[data-node-id]');\n const isWaypoint = target.classList.contains('pipe-route-point');\n const isPipeSegment = target.classList.contains('pipe-segment-add') || target.classList.contains('pipe-segment-add-area');\n \n if (isDraggableNode || isWaypoint || isPipeSegment) {\n return; // Let drag handler take over\n }\n\n // Left button (if allowed), middle button, or space + left button\n const isLeftClick = e.button === 0 && allowLeftClickPan;\n const isMiddleClick = e.button === 1;\n const isSpaceLeftClick = e.button === 0 && spaceKeyDownRef.current;\n \n if (isLeftClick || isMiddleClick || isSpaceLeftClick) {\n e.preventDefault();\n isPanningRef.current = true;\n lastMousePosRef.current = { x: e.clientX, y: e.clientY };\n svg.style.cursor = 'grabbing';\n }\n };\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!isPanningRef.current) return;\n\n const dx = e.clientX - lastMousePosRef.current.x;\n const dy = e.clientY - lastMousePosRef.current.y;\n lastMousePosRef.current = { x: e.clientX, y: e.clientY };\n\n setViewBox((prev) => {\n const rect = svg.getBoundingClientRect();\n const scaleX = prev.width / rect.width;\n const scaleY = prev.height / rect.height;\n\n return {\n ...prev,\n x: prev.x - dx * scaleX,\n y: prev.y - dy * scaleY,\n };\n });\n };\n\n const handleMouseUp = () => {\n if (isPanningRef.current) {\n isPanningRef.current = false;\n svg.style.cursor = (allowLeftClickPan || spaceKeyDownRef.current) ? 'grab' : '';\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.code === 'Space' && !spaceKeyDownRef.current) {\n e.preventDefault();\n spaceKeyDownRef.current = true;\n if (!isPanningRef.current && !allowLeftClickPan) {\n svg.style.cursor = 'grab';\n }\n }\n };\n\n const handleKeyUp = (e: KeyboardEvent) => {\n if (e.code === 'Space') {\n spaceKeyDownRef.current = false;\n if (!isPanningRef.current && !allowLeftClickPan) {\n svg.style.cursor = '';\n }\n }\n };\n\n // Set initial cursor if left-click pan is enabled\n if (allowLeftClickPan && !isPanningRef.current) {\n svg.style.cursor = 'grab';\n }\n\n svg.addEventListener('mousedown', handleMouseDown);\n window.addEventListener('mousemove', handleMouseMove);\n window.addEventListener('mouseup', handleMouseUp);\n window.addEventListener('keydown', handleKeyDown);\n window.addEventListener('keyup', handleKeyUp);\n\n return () => {\n svg.removeEventListener('mousedown', handleMouseDown);\n window.removeEventListener('mousemove', handleMouseMove);\n window.removeEventListener('mouseup', handleMouseUp);\n window.removeEventListener('keydown', handleKeyDown);\n window.removeEventListener('keyup', handleKeyUp);\n };\n }, [enablePan, allowLeftClickPan]);\n\n // Handle touch gestures (pan and pinch-to-zoom)\n useEffect(() => {\n if (!svgRef.current) return;\n if (!enablePan && !enableZoom) return;\n\n const svg = svgRef.current;\n\n const handleTouchStart = (e: TouchEvent) => {\n // Check if touching a draggable element (node, waypoint, pipe segment)\n const target = e.target as Element;\n const isDraggableNode = target.closest('[data-node-id]');\n const isWaypoint = target.classList.contains('pipe-route-point');\n const isPipeSegment = target.classList.contains('pipe-segment-add') || target.classList.contains('pipe-segment-add-area');\n \n if ((isDraggableNode || isWaypoint || isPipeSegment) && e.touches.length === 1) {\n return; // Let drag handler take over for single touch\n }\n\n if (e.touches.length === 1 && enablePan) {\n // Single touch - pan\n isPanningRef.current = true;\n lastMousePosRef.current = {\n x: e.touches[0].clientX,\n y: e.touches[0].clientY,\n };\n } else if (e.touches.length === 2 && enableZoom) {\n // Two touches - prepare for pinch zoom\n const touch1 = e.touches[0];\n const touch2 = e.touches[1];\n const distance = Math.hypot(\n touch2.clientX - touch1.clientX,\n touch2.clientY - touch1.clientY\n );\n lastTouchDistanceRef.current = distance;\n }\n };\n\n const handleTouchMove = (e: TouchEvent) => {\n e.preventDefault();\n\n if (e.touches.length === 1 && isPanningRef.current && enablePan) {\n // Single touch pan\n const touch = e.touches[0];\n const dx = touch.clientX - lastMousePosRef.current.x;\n const dy = touch.clientY - lastMousePosRef.current.y;\n lastMousePosRef.current = { x: touch.clientX, y: touch.clientY };\n\n setViewBox((prev) => {\n const rect = svg.getBoundingClientRect();\n const scaleX = prev.width / rect.width;\n const scaleY = prev.height / rect.height;\n\n return {\n ...prev,\n x: prev.x - dx * scaleX,\n y: prev.y - dy * scaleY,\n };\n });\n } else if (e.touches.length === 2 && enableZoom && lastTouchDistanceRef.current) {\n // Pinch zoom\n const touch1 = e.touches[0];\n const touch2 = e.touches[1];\n const distance = Math.hypot(\n touch2.clientX - touch1.clientX,\n touch2.clientY - touch1.clientY\n );\n\n const scale = distance / lastTouchDistanceRef.current;\n lastTouchDistanceRef.current = distance;\n\n // Get center point between two touches\n const centerX = (touch1.clientX + touch2.clientX) / 2;\n const centerY = (touch1.clientY + touch2.clientY) / 2;\n const rect = svg.getBoundingClientRect();\n\n setViewBox((prev) => {\n // Convert touch center to viewBox coordinates\n const svgX = prev.x + ((centerX - rect.left) / rect.width) * prev.width;\n const svgY = prev.y + ((centerY - rect.top) / rect.height) * prev.height;\n\n // Calculate new dimensions\n let newWidth = prev.width / scale;\n let newHeight = prev.height / scale;\n\n // Clamp zoom level\n const currentZoom = initialViewBox.width / prev.width;\n const newZoom = currentZoom * scale;\n if (newZoom < minZoom || newZoom > 1 / minZoom) {\n return prev;\n }\n\n // Adjust position to zoom toward touch center\n const newX = svgX - ((centerX - rect.left) / rect.width) * newWidth;\n const newY = svgY - ((centerY - rect.top) / rect.height) * newHeight;\n\n return {\n x: newX,\n y: newY,\n width: newWidth,\n height: newHeight,\n };\n });\n }\n };\n\n const handleTouchEnd = () => {\n isPanningRef.current = false;\n lastTouchDistanceRef.current = null;\n };\n\n svg.addEventListener('touchstart', handleTouchStart);\n svg.addEventListener('touchmove', handleTouchMove, { passive: false });\n svg.addEventListener('touchend', handleTouchEnd);\n\n return () => {\n svg.removeEventListener('touchstart', handleTouchStart);\n svg.removeEventListener('touchmove', handleTouchMove);\n svg.removeEventListener('touchend', handleTouchEnd);\n };\n }, [enablePan, enableZoom, minZoom, initialViewBox]);\n\n const screenToWorld = useCallback(\n (screenX: number, screenY: number): { x: number; y: number } => {\n const svg = svgRef.current;\n if (!svg) return { x: screenX, y: screenY };\n\n const rect = svg.getBoundingClientRect();\n \n // Account for preserveAspectRatio=\"xMidYMid meet\"\n const svgAspect = rect.width / rect.height;\n const viewBoxAspect = viewBox.width / viewBox.height;\n \n let actualWidth = rect.width;\n let actualHeight = rect.height;\n let offsetX = 0;\n let offsetY = 0;\n \n if (svgAspect > viewBoxAspect) {\n // SVG is wider - pillarboxing on sides\n actualWidth = rect.height * viewBoxAspect;\n offsetX = (rect.width - actualWidth) / 2;\n } else {\n // SVG is taller - letterboxing on top/bottom\n actualHeight = rect.width / viewBoxAspect;\n offsetY = (rect.height - actualHeight) / 2;\n }\n \n const relativeX = (screenX - rect.left - offsetX) / actualWidth;\n const relativeY = (screenY - rect.top - offsetY) / actualHeight;\n\n const worldX = viewBox.x + relativeX * viewBox.width;\n const worldY = viewBox.y + relativeY * viewBox.height;\n\n return { x: worldX, y: worldY };\n },\n [viewBox]\n );\n\n return {\n viewBox,\n resetViewBox,\n zoomIn,\n zoomOut,\n fitToContent,\n setViewBox,\n screenToWorld,\n svgRef,\n };\n}\n","import { useState, useCallback } from 'react';\n\nexport interface SelectionState {\n /** IDs of selected nodes */\n selectedNodeIds: Set<string>;\n /** IDs of selected pipes */\n selectedPipeIds: Set<string>;\n}\n\nexport interface UseSelectionOptions {\n /** Allow multiple selection with Ctrl/Cmd key (default: true) */\n multiSelect?: boolean;\n /** Initial selection state */\n initialSelection?: SelectionState;\n /** Callback when selection changes */\n onSelectionChange?: (selection: SelectionState) => void;\n}\n\nexport interface UseSelectionResult {\n /** Current selection state */\n selection: SelectionState;\n /** Check if a node is selected */\n isNodeSelected: (nodeId: string) => boolean;\n /** Check if a pipe is selected */\n isPipeSelected: (pipeId: string) => boolean;\n /** Select a node */\n selectNode: (nodeId: string, multiSelect?: boolean) => void;\n /** Select a pipe */\n selectPipe: (pipeId: string, multiSelect?: boolean) => void;\n /** Clear all selections */\n clearSelection: () => void;\n /** Select multiple nodes */\n selectNodes: (nodeIds: string[]) => void;\n /** Select multiple pipes */\n selectPipes: (pipeIds: string[]) => void;\n}\n\n/**\n * Custom hook for managing selection state in P&ID diagrams\n * \n * Supports:\n * - Single selection (click)\n * - Multi-selection (Ctrl+click)\n * - Programmatic selection\n * - Selection callbacks\n */\nexport function useSelection(options: UseSelectionOptions = {}): UseSelectionResult {\n const {\n multiSelect = true,\n initialSelection = { selectedNodeIds: new Set(), selectedPipeIds: new Set() },\n onSelectionChange,\n } = options;\n\n const [selection, setSelection] = useState<SelectionState>(initialSelection);\n\n const notifyChange = useCallback(\n (newSelection: SelectionState) => {\n if (onSelectionChange) {\n onSelectionChange(newSelection);\n }\n },\n [onSelectionChange]\n );\n\n const isNodeSelected = useCallback(\n (nodeId: string) => selection.selectedNodeIds.has(nodeId),\n [selection.selectedNodeIds]\n );\n\n const isPipeSelected = useCallback(\n (pipeId: string) => selection.selectedPipeIds.has(pipeId),\n [selection.selectedPipeIds]\n );\n\n const selectNode = useCallback(\n (nodeId: string, isMultiSelect = false) => {\n setSelection((prev) => {\n const newNodeIds = new Set<string>(isMultiSelect && multiSelect ? prev.selectedNodeIds : []);\n \n if (newNodeIds.has(nodeId)) {\n // Deselect if already selected in multi-select mode\n newNodeIds.delete(nodeId);\n } else {\n newNodeIds.add(nodeId);\n }\n\n const newSelection: SelectionState = {\n selectedNodeIds: newNodeIds,\n selectedPipeIds: isMultiSelect && multiSelect ? prev.selectedPipeIds : new Set<string>(),\n };\n\n notifyChange(newSelection);\n return newSelection;\n });\n },\n [multiSelect, notifyChange]\n );\n\n const selectPipe = useCallback(\n (pipeId: string, isMultiSelect = false) => {\n setSelection((prev) => {\n const newPipeIds = new Set<string>(isMultiSelect && multiSelect ? prev.selectedPipeIds : []);\n \n if (newPipeIds.has(pipeId)) {\n // Deselect if already selected in multi-select mode\n newPipeIds.delete(pipeId);\n } else {\n newPipeIds.add(pipeId);\n }\n\n const newSelection: SelectionState = {\n selectedNodeIds: isMultiSelect && multiSelect ? prev.selectedNodeIds : new Set<string>(),\n selectedPipeIds: newPipeIds,\n };\n\n notifyChange(newSelection);\n return newSelection;\n });\n },\n [multiSelect, notifyChange]\n );\n\n const clearSelection = useCallback(() => {\n const newSelection = {\n selectedNodeIds: new Set<string>(),\n selectedPipeIds: new Set<string>(),\n };\n setSelection(newSelection);\n notifyChange(newSelection);\n }, [notifyChange]);\n\n const selectNodes = useCallback(\n (nodeIds: string[]) => {\n const newSelection = {\n selectedNodeIds: new Set(nodeIds),\n selectedPipeIds: new Set<string>(),\n };\n setSelection(newSelection);\n notifyChange(newSelection);\n },\n [notifyChange]\n );\n\n const selectPipes = useCallback(\n (pipeIds: string[]) => {\n const newSelection = {\n selectedNodeIds: new Set<string>(),\n selectedPipeIds: new Set(pipeIds),\n };\n setSelection(newSelection);\n notifyChange(newSelection);\n },\n [notifyChange]\n );\n\n return {\n selection,\n isNodeSelected,\n isPipeSelected,\n selectNode,\n selectPipe,\n clearSelection,\n selectNodes,\n selectPipes,\n };\n}\n","import { useState, useCallback, useEffect, useRef } from 'react';\nimport type { TelemetryBinding, TelemetryMap, TelemetryValue } from '../schema/TelemetryBinding';\n\n// Re-export types for consumer convenience\nexport type { TelemetryBinding, TelemetryMap, TelemetryValue };\n\nexport interface UseTelemetryOptions {\n /** Initial telemetry bindings */\n initialBindings?: TelemetryBinding[];\n /** Callback when telemetry values change */\n onTelemetryChange?: (bindings: TelemetryMap) => void;\n /** Auto-refresh interval in milliseconds (0 = disabled) */\n refreshInterval?: number;\n}\n\nexport interface UseTelemetryResult {\n /** Current telemetry bindings map */\n telemetry: TelemetryMap;\n /** Update a single telemetry value */\n updateTelemetry: (nodeId: string, value: TelemetryValue) => void;\n /** Update multiple telemetry values at once */\n updateTelemetryBatch: (updates: Array<{ nodeId: string; value: TelemetryValue; history?: number[] }>) => void;\n /** Get telemetry for a specific node */\n getTelemetry: (nodeId: string) => TelemetryBinding | undefined;\n /** Clear all telemetry data */\n clearTelemetry: () => void;\n /** Set telemetry bindings */\n setBindings: (bindings: TelemetryBinding[]) => void;\n}\n\n/**\n * Custom hook for managing telemetry data bindings\n * \n * Supports:\n * - Real-time value updates\n * - Status monitoring (normal, warning, alarm, fault, off)\n * - Batch updates for performance\n * - Auto-refresh capability\n * - Quality indicators\n */\nexport function useTelemetry(options: UseTelemetryOptions = {}): UseTelemetryResult {\n const {\n initialBindings = [],\n onTelemetryChange,\n refreshInterval = 0,\n } = options;\n\n // Initialize telemetry map from bindings\n const [telemetry, setTelemetry] = useState<TelemetryMap>(() => {\n const map = new Map<string, TelemetryBinding>();\n initialBindings.forEach((binding) => {\n map.set(binding.nodeId, binding);\n });\n return map;\n });\n\n const telemetryRef = useRef(telemetry);\n telemetryRef.current = telemetry;\n\n const notifyChange = useCallback(\n (newTelemetry: TelemetryMap) => {\n if (onTelemetryChange) {\n onTelemetryChange(newTelemetry);\n }\n },\n [onTelemetryChange]\n );\n\n const updateTelemetry = useCallback(\n (nodeId: string, value: TelemetryValue) => {\n setTelemetry((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n \n if (existing) {\n // Update existing binding\n newMap.set(nodeId, {\n ...existing,\n value: {\n ...value,\n timestamp: value.timestamp ?? Date.now(),\n },\n });\n } else {\n // Create new binding\n newMap.set(nodeId, {\n nodeId,\n tag: nodeId, // Default tag to nodeId\n value: {\n ...value,\n timestamp: value.timestamp ?? Date.now(),\n },\n });\n }\n\n notifyChange(newMap);\n return newMap;\n });\n },\n [notifyChange]\n );\n\n const updateTelemetryBatch = useCallback(\n (updates: Array<{ \n nodeId: string; \n tag?: string; \n value?: TelemetryValue; \n history?: number[]; \n display?: any; \n thresholds?: any; \n statusColors?: any;\n remove?: boolean \n }>) => {\n setTelemetry((prev) => {\n const newMap = new Map(prev);\n let hasChanges = false;\n\n updates.forEach(({ nodeId, tag, value, history, display, thresholds, statusColors, remove }) => {\n if (remove) {\n // Remove telemetry binding\n if (newMap.has(nodeId)) {\n newMap.delete(nodeId);\n hasChanges = true;\n }\n return;\n }\n\n const existing = newMap.get(nodeId);\n\n if (existing) {\n newMap.set(nodeId, {\n ...existing,\n ...(tag !== undefined && { tag }),\n ...(value !== undefined && {\n value: {\n ...value,\n timestamp: value.timestamp ?? Date.now(),\n },\n }),\n ...(history !== undefined && { history }),\n ...(display !== undefined && { display }),\n ...(thresholds !== undefined && { thresholds }),\n ...(statusColors !== undefined && { statusColors }),\n });\n hasChanges = true;\n } else if (value) {\n newMap.set(nodeId, {\n nodeId,\n tag: tag ?? nodeId,\n value: {\n ...value,\n timestamp: value.timestamp ?? Date.now(),\n },\n ...(history !== undefined && { history }),\n ...(display !== undefined && { display }),\n ...(thresholds !== undefined && { thresholds }),\n ...(statusColors !== undefined && { statusColors }),\n });\n hasChanges = true;\n }\n });\n\n if (hasChanges) {\n notifyChange(newMap);\n }\n return newMap;\n });\n },\n [notifyChange]\n );\n\n const getTelemetry = useCallback(\n (nodeId: string) => telemetryRef.current.get(nodeId),\n []\n );\n\n const clearTelemetry = useCallback(() => {\n const newMap = new Map<string, TelemetryBinding>();\n setTelemetry(newMap);\n notifyChange(newMap);\n }, [notifyChange]);\n\n const setBindings = useCallback(\n (bindings: TelemetryBinding[]) => {\n const newMap = new Map<string, TelemetryBinding>();\n bindings.forEach((binding) => {\n newMap.set(binding.nodeId, binding);\n });\n setTelemetry(newMap);\n notifyChange(newMap);\n },\n [notifyChange]\n );\n\n // Auto-refresh functionality\n useEffect(() => {\n if (refreshInterval <= 0) return;\n\n const intervalId = setInterval(() => {\n // Trigger change notification to allow external refresh logic\n notifyChange(telemetryRef.current);\n }, refreshInterval);\n\n return () => clearInterval(intervalId);\n }, [refreshInterval, notifyChange]);\n\n return {\n telemetry,\n updateTelemetry,\n updateTelemetryBatch,\n getTelemetry,\n clearTelemetry,\n setBindings,\n };\n}\n","import { useEffect } from 'react';\nimport type { TelemetryMap } from './useTelemetry';\nimport type { Diagram } from '../schema';\nimport type { TelemetryStatus } from '../schema/TelemetryBinding';\n\n/**\n * Options for syncing telemetry status to node status\n */\nexport interface UseTelemetryStatusOptions {\n /** Whether status sync is enabled (default: true) */\n enabled?: boolean;\n /**\n * Custom status mapping function.\n *\n * A custom mapper replaces the default entirely. It must handle every\n * member of `TelemetryStatus`, including `unknown`. A mapper that returns\n * a healthy-looking status for `unknown` reintroduces the defect the\n * default mapping closes.\n */\n mapStatus?: (telemetryStatus: TelemetryStatus) => string;\n}\n\n/**\n * Hook to sync telemetry status to node instance status for dynamic coloring\n * \n * Updates node.status based on telemetry status values:\n * - 'normal' → 'running'\n * - 'warning' → 'warning'\n * - 'alarm' / 'fault' → 'alarm'\n * - 'off' → 'stopped'\n * - 'unknown' → 'unknown' (untrusted read; must not read as stopped)\n * \n * @param telemetry - Telemetry map from useTelemetry\n * @param diagram - Current diagram state\n * @param setDiagram - Diagram state setter\n * @param options - Configuration options\n * \n * @example\n * ```tsx\n * const { telemetry } = useTelemetry();\n * const [diagram, setDiagram] = useState<Diagram>({...});\n * \n * useTelemetryStatus(telemetry, diagram, setDiagram, {\n * enabled: mode === 'runtime'\n * });\n * ```\n */\nexport function useTelemetryStatus(\n telemetry: TelemetryMap,\n diagram: Diagram,\n setDiagram: (diagram: Diagram) => void,\n options: UseTelemetryStatusOptions = {}\n) {\n const { enabled = true, mapStatus } = options;\n\n useEffect(() => {\n if (!enabled) return;\n\n // Map telemetry status to node status\n const defaultMapStatus = (telemetryStatus: TelemetryStatus): string => {\n switch (telemetryStatus) {\n case 'normal':\n return 'running';\n case 'warning':\n return 'warning';\n case 'alarm':\n case 'fault':\n return 'alarm';\n case 'off':\n return 'stopped';\n case 'unknown':\n // An untrusted read is not a confirmed-off device. Keep it distinct\n // so the symbol does not paint with the OFF colours.\n return 'unknown';\n default: {\n // Compile-time guard: this assignment fails to build if a new\n // TelemetryStatus member has no case above. Without it, a future\n // status would silently paint as 'stopped', which is the defect\n // the 'unknown' case exists to close.\n const exhaustive: never = telemetryStatus;\n void exhaustive;\n // Runtime last resort for a value that arrived from outside the\n // type system.\n return 'stopped';\n }\n }\n };\n\n const statusMapper = mapStatus || defaultMapStatus;\n\n // Check if any node status needs updating\n let needsUpdate = false;\n const updatedNodes = diagram.nodes.map((node) => {\n const binding = telemetry.get(node.id);\n if (!binding) return node;\n\n const newStatus = statusMapper(binding.value.status);\n if (node.status !== newStatus) {\n needsUpdate = true;\n return { ...node, status: newStatus };\n }\n return node;\n });\n\n if (needsUpdate) {\n setDiagram({\n ...diagram,\n nodes: updatedNodes,\n });\n }\n }, [telemetry, diagram, setDiagram, enabled, mapStatus]);\n}\n","import { useEffect } from 'react';\nimport type { TelemetryMap } from './useTelemetry';\nimport type { Diagram } from '../schema';\nimport { resolvePresetColor } from '../symbols/appearanceSchema';\n\n/** Options for the signal-binding sync. */\nexport interface UseBooleanIndicatorsOptions {\n /** Whether the sync is enabled (default: true). Disable in edit mode. */\n enabled?: boolean;\n /** Symbol id treated as a status indicator (default: 'status-indicator'). */\n symbolId?: string;\n}\n\n/**\n * Drive a status-indicator node's color and visibility from independent named\n * signals.\n *\n * Each indicator node names two tags in its metadata:\n * - `colorSource` — a tag whose truthy value shows `onColor`, falsy shows `offColor`.\n * - `visibleSource` — a tag whose truthy value shows the node, falsy hides it.\n *\n * Both are resolved against a tag → value index built from every published\n * telemetry binding, so color and visibility can follow completely different\n * signals. A blank source leaves that property alone (color unbound, visibility\n * static). Writes `customColors.fill` and `visible` — never touches the renderer\n * props, so the NodeRenderer memo boundary stays intact.\n *\n * @example\n * ```tsx\n * useBooleanIndicators(telemetry, diagram, setDiagram, { enabled: mode === 'runtime' });\n * ```\n */\nexport function useBooleanIndicators(\n telemetry: TelemetryMap,\n diagram: Diagram,\n setDiagram: (diagram: Diagram) => void,\n options: UseBooleanIndicatorsOptions = {}\n) {\n const { enabled = true, symbolId = 'status-indicator' } = options;\n\n useEffect(() => {\n if (!enabled) return;\n\n // Index every published signal by its tag, so any node can resolve any tag.\n const tagIndex = new Map<string, number | string | boolean>();\n telemetry.forEach((binding) => {\n if (binding.tag) tagIndex.set(binding.tag, binding.value.value);\n });\n\n let needsUpdate = false;\n const updatedNodes = diagram.nodes.map((node) => {\n if (node.symbolId !== symbolId) return node;\n\n const meta = node.metadata ?? {};\n const colorSource = typeof meta.colorSource === 'string' ? meta.colorSource : '';\n const visibleSource = typeof meta.visibleSource === 'string' ? meta.visibleSource : '';\n\n let next = node;\n\n if (colorSource) {\n const isTrue = Boolean(tagIndex.get(colorSource));\n const fill = resolvePresetColor(\n String(isTrue ? meta.onColor ?? 'green' : meta.offColor ?? 'red')\n );\n if (node.customColors?.fill !== fill) {\n next = { ...next, customColors: { ...next.customColors, fill } };\n }\n }\n\n if (visibleSource) {\n const vis = Boolean(tagIndex.get(visibleSource));\n if ((next.visible ?? true) !== vis) {\n next = { ...next, visible: vis };\n }\n }\n\n if (next !== node) needsUpdate = true;\n return next;\n });\n\n if (needsUpdate) {\n setDiagram({ ...diagram, nodes: updatedNodes });\n }\n }, [telemetry, diagram, setDiagram, enabled, symbolId]);\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\n\nexport interface DragPoint {\n x: number;\n y: number;\n}\n\nexport interface DragState {\n /** Whether drag is currently active */\n isDragging: boolean;\n /** Initial position when drag started */\n startPosition: DragPoint | null;\n /** Current drag offset from start */\n offset: DragPoint;\n /** ID of the item being dragged */\n draggedId: string | null;\n}\n\nexport interface UseDragOptions {\n /** Callback when drag starts */\n onDragStart?: (id: string, position: DragPoint) => void;\n /** Callback during drag movement */\n onDragMove?: (id: string, offset: DragPoint, position: DragPoint) => void;\n /** Callback when drag ends */\n onDragEnd?: (id: string, offset: DragPoint, finalPosition: DragPoint) => void;\n /** Snap to grid size (0 = no snap) */\n snapToGrid?: number;\n /** Minimum drag distance before triggering (prevents accidental drags) */\n dragThreshold?: number;\n /** Convert screen coordinates to world coordinates */\n screenToWorld?: (screenX: number, screenY: number) => DragPoint;\n}\n\nexport interface UseDragResult {\n /** Current drag state */\n dragState: DragState;\n /** Start dragging an item */\n startDrag: (id: string, event: React.MouseEvent | React.TouchEvent, itemPosition: DragPoint) => void;\n /** Cancel current drag */\n cancelDrag: () => void;\n /** Check if a specific item is being dragged */\n isDraggingItem: (id: string) => boolean;\n}\n\n/**\n * Custom hook for drag-and-drop functionality in P&ID diagrams\n * \n * Supports:\n * - Mouse drag\n * - Touch drag\n * - Snap-to-grid\n * - World coordinate conversion (for SVG viewBox)\n * - Drag threshold to prevent accidental drags\n */\nexport function useDrag(options: UseDragOptions = {}): UseDragResult {\n const {\n onDragStart,\n onDragMove,\n onDragEnd,\n snapToGrid = 0,\n dragThreshold = 3,\n screenToWorld = (x, y) => ({ x, y }),\n } = options;\n\n const [dragState, setDragState] = useState<DragState>({\n isDragging: false,\n startPosition: null,\n offset: { x: 0, y: 0 },\n draggedId: null,\n });\n\n const [isListening, setIsListening] = useState(false);\n const dragStartRef = useRef<{ id: string; screenStart: DragPoint; worldStart: DragPoint; itemPosition: DragPoint; clickOffset: DragPoint } | null>(null);\n const hasMovedRef = useRef(false);\n\n const applySnap = useCallback(\n (value: number): number => {\n if (snapToGrid <= 0) return value;\n return Math.round(value / snapToGrid) * snapToGrid;\n },\n [snapToGrid]\n );\n\n const handleMouseMove = useCallback(\n (event: MouseEvent) => {\n if (!dragStartRef.current) return;\n\n const { id, screenStart: _screenStart, itemPosition, clickOffset } = dragStartRef.current;\n const currentWorld = screenToWorld(event.clientX, event.clientY);\n\n // New position = current cursor - click offset\n const newX = currentWorld.x - clickOffset.x;\n const newY = currentWorld.y - clickOffset.y;\n\n const rawOffset = {\n x: newX - itemPosition.x,\n y: newY - itemPosition.y,\n };\n\n // Check if we've exceeded the drag threshold\n const distance = Math.sqrt(rawOffset.x ** 2 + rawOffset.y ** 2);\n if (!hasMovedRef.current && distance < dragThreshold) {\n return;\n }\n\n if (!hasMovedRef.current) {\n hasMovedRef.current = true;\n setDragState({\n isDragging: true,\n startPosition: itemPosition,\n offset: { x: 0, y: 0 },\n draggedId: id,\n });\n onDragStart?.(id, itemPosition);\n }\n\n const snappedOffset = {\n x: applySnap(rawOffset.x),\n y: applySnap(rawOffset.y),\n };\n\n const newPosition = {\n x: applySnap(itemPosition.x + rawOffset.x),\n y: applySnap(itemPosition.y + rawOffset.y),\n };\n\n setDragState((prev) => ({\n ...prev,\n offset: snappedOffset,\n }));\n\n onDragMove?.(id, snappedOffset, newPosition);\n },\n [screenToWorld, dragThreshold, applySnap, onDragStart, onDragMove]\n );\n\n const handleMouseUp = useCallback(\n (event: MouseEvent) => {\n if (!dragStartRef.current || !hasMovedRef.current) {\n dragStartRef.current = null;\n hasMovedRef.current = false;\n setIsListening(false);\n return;\n }\n\n const { id, itemPosition, clickOffset } = dragStartRef.current;\n const currentWorld = screenToWorld(event.clientX, event.clientY);\n\n const newX = currentWorld.x - clickOffset.x;\n const newY = currentWorld.y - clickOffset.y;\n\n const rawOffset = {\n x: newX - itemPosition.x,\n y: newY - itemPosition.y,\n };\n\n const snappedOffset = {\n x: applySnap(rawOffset.x),\n y: applySnap(rawOffset.y),\n };\n\n const finalPosition = {\n x: applySnap(itemPosition.x + rawOffset.x),\n y: applySnap(itemPosition.y + rawOffset.y),\n };\n\n onDragEnd?.(id, snappedOffset, finalPosition);\n\n setDragState({\n isDragging: false,\n startPosition: null,\n offset: { x: 0, y: 0 },\n draggedId: null,\n });\n\n dragStartRef.current = null;\n hasMovedRef.current = false;\n setIsListening(false);\n },\n [screenToWorld, applySnap, onDragEnd]\n );\n\n const handleTouchMove = useCallback(\n (event: TouchEvent) => {\n if (!dragStartRef.current || event.touches.length !== 1) return;\n\n const touch = event.touches[0];\n const { id, screenStart: _screenStart2, itemPosition, clickOffset } = dragStartRef.current;\n const currentWorld = screenToWorld(touch.clientX, touch.clientY);\n\n const newX = currentWorld.x - clickOffset.x;\n const newY = currentWorld.y - clickOffset.y;\n\n const rawOffset = {\n x: newX - itemPosition.x,\n y: newY - itemPosition.y,\n };\n\n const distance = Math.sqrt(rawOffset.x ** 2 + rawOffset.y ** 2);\n if (!hasMovedRef.current && distance < dragThreshold) {\n return;\n }\n\n event.preventDefault(); // Prevent scrolling\n\n if (!hasMovedRef.current) {\n hasMovedRef.current = true;\n setDragState({\n isDragging: true,\n startPosition: itemPosition,\n offset: { x: 0, y: 0 },\n draggedId: id,\n });\n onDragStart?.(id, itemPosition);\n }\n\n const snappedOffset = {\n x: applySnap(rawOffset.x),\n y: applySnap(rawOffset.y),\n };\n\n const newPosition = {\n x: applySnap(itemPosition.x + rawOffset.x),\n y: applySnap(itemPosition.y + rawOffset.y),\n };\n\n setDragState((prev) => ({\n ...prev,\n offset: snappedOffset,\n }));\n\n onDragMove?.(id, snappedOffset, newPosition);\n },\n [screenToWorld, dragThreshold, applySnap, onDragStart, onDragMove]\n );\n\n const handleTouchEnd = useCallback(\n (event: TouchEvent) => {\n if (!dragStartRef.current || !hasMovedRef.current) {\n dragStartRef.current = null;\n hasMovedRef.current = false;\n setIsListening(false);\n return;\n }\n\n const { id, itemPosition, clickOffset } = dragStartRef.current;\n const touch = event.changedTouches[0];\n const currentWorld = screenToWorld(touch.clientX, touch.clientY);\n\n const newX = currentWorld.x - clickOffset.x;\n const newY = currentWorld.y - clickOffset.y;\n\n const rawOffset = {\n x: newX - itemPosition.x,\n y: newY - itemPosition.y,\n };\n\n const snappedOffset = {\n x: applySnap(rawOffset.x),\n y: applySnap(rawOffset.y),\n };\n\n const finalPosition = {\n x: applySnap(itemPosition.x + rawOffset.x),\n y: applySnap(itemPosition.y + rawOffset.y),\n };\n\n onDragEnd?.(id, snappedOffset, finalPosition);\n\n setDragState({\n isDragging: false,\n startPosition: null,\n offset: { x: 0, y: 0 },\n draggedId: null,\n });\n\n dragStartRef.current = null;\n hasMovedRef.current = false;\n setIsListening(false);\n },\n [screenToWorld, applySnap, onDragEnd]\n );\n\n useEffect(() => {\n // Only attach listeners when actively listening for drag events\n if (!isListening) return;\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n document.addEventListener('touchmove', handleTouchMove, { passive: false });\n document.addEventListener('touchend', handleTouchEnd);\n document.addEventListener('touchcancel', handleTouchEnd);\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n document.removeEventListener('touchmove', handleTouchMove);\n document.removeEventListener('touchend', handleTouchEnd);\n document.removeEventListener('touchcancel', handleTouchEnd);\n };\n }, [isListening, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]);\n\n const startDrag = useCallback(\n (id: string, event: React.MouseEvent | React.TouchEvent, itemPosition: DragPoint) => {\n event.stopPropagation();\n\n let clientX: number;\n let clientY: number;\n\n if ('touches' in event) {\n if (event.touches.length !== 1) return;\n clientX = event.touches[0].clientX;\n clientY = event.touches[0].clientY;\n } else {\n clientX = event.clientX;\n clientY = event.clientY;\n }\n\n const worldStart = screenToWorld(clientX, clientY);\n \n // Calculate offset from click position to item position\n const clickOffset = {\n x: worldStart.x - itemPosition.x,\n y: worldStart.y - itemPosition.y,\n };\n\n dragStartRef.current = {\n id,\n screenStart: { x: clientX, y: clientY },\n worldStart,\n itemPosition,\n clickOffset,\n };\n hasMovedRef.current = false;\n setIsListening(true); // Start listening for move/up events\n },\n [screenToWorld]\n );\n\n const cancelDrag = useCallback(() => {\n dragStartRef.current = null;\n hasMovedRef.current = false;\n setIsListening(false);\n setDragState({\n isDragging: false,\n startPosition: null,\n offset: { x: 0, y: 0 },\n draggedId: null,\n });\n }, []);\n\n const isDraggingItem = useCallback(\n (id: string) => dragState.isDragging && dragState.draggedId === id,\n [dragState.isDragging, dragState.draggedId]\n );\n\n return {\n dragState,\n startDrag,\n cancelDrag,\n isDraggingItem,\n };\n}\n","import { useEffect } from 'react';\n\nexport interface KeyboardShortcutsOptions {\n /** Callback when Delete or Backspace is pressed */\n onDelete?: () => void;\n /** Callback when Ctrl+C is pressed */\n onCopy?: () => void;\n /** Callback when Ctrl+V is pressed */\n onPaste?: () => void;\n /** Callback when Ctrl+Z is pressed (undo) */\n onUndo?: () => void;\n /** Callback when Ctrl+Y or Ctrl+Shift+Z is pressed (redo) */\n onRedo?: () => void;\n /** Callback when Ctrl+A is pressed (select all) */\n onSelectAll?: () => void;\n /** Enable keyboard shortcuts (default: true) */\n enabled?: boolean;\n}\n\n/**\n * Custom hook for handling keyboard shortcuts in P&ID diagrams\n * \n * Supported shortcuts:\n * - Delete/Backspace: Delete selected items\n * - Ctrl+C: Copy selected items\n * - Ctrl+V: Paste copied items\n * - Ctrl+Z: Undo last action\n * - Ctrl+Y / Ctrl+Shift+Z: Redo\n * - Ctrl+A: Select all\n */\nexport function useKeyboardShortcuts(options: KeyboardShortcutsOptions): void {\n const {\n onDelete,\n onCopy,\n onPaste,\n onUndo,\n onRedo,\n onSelectAll,\n enabled = true,\n } = options;\n\n useEffect(() => {\n if (!enabled) return;\n\n const handleKeyDown = (event: KeyboardEvent) => {\n // Ignore if user is typing in an input field\n const target = event.target as HTMLElement;\n if (\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.isContentEditable\n ) {\n return;\n }\n\n const isCtrl = event.ctrlKey || event.metaKey;\n const isShift = event.shiftKey;\n\n // Delete or Backspace\n if ((event.key === 'Delete' || event.key === 'Backspace') && !isCtrl) {\n event.preventDefault();\n onDelete?.();\n return;\n }\n\n // Ctrl+C - Copy\n if (isCtrl && event.key === 'c') {\n event.preventDefault();\n onCopy?.();\n return;\n }\n\n // Ctrl+V - Paste\n if (isCtrl && event.key === 'v') {\n event.preventDefault();\n onPaste?.();\n return;\n }\n\n // Ctrl+Z - Undo\n if (isCtrl && event.key === 'z' && !isShift) {\n event.preventDefault();\n onUndo?.();\n return;\n }\n\n // Ctrl+Y or Ctrl+Shift+Z - Redo\n if (isCtrl && (event.key === 'y' || (event.key === 'z' && isShift))) {\n event.preventDefault();\n onRedo?.();\n return;\n }\n\n // Ctrl+A - Select all\n if (isCtrl && event.key === 'a') {\n event.preventDefault();\n onSelectAll?.();\n return;\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [enabled, onDelete, onCopy, onPaste, onUndo, onRedo, onSelectAll]);\n}\n","import type { NodeInstance, Point } from '../schema';\nimport { getSymbolDefinition } from '../symbols';\n\n/**\n * Calculate the world position of a port on a node instance\n * \n * Takes into account the node's transform (position, rotation, scale, flip)\n * and the port's position within the symbol's viewBox\n */\nexport function getPortWorldPosition(\n node: NodeInstance,\n portId: string\n): Point | null {\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol) return null;\n\n const port = symbol.ports.find((p) => p.id === portId);\n if (!port) return null;\n\n const { x: nodeX, y: nodeY, rotation = 0, scale = 1, flipX = false, flipY = false } = node.transform;\n\n // Start with port position relative to symbol\n let portX = port.x;\n let portY = port.y;\n\n // Apply flip transforms (relative to symbol center)\n const centerX = symbol.viewBox.width / 2;\n const centerY = symbol.viewBox.height / 2;\n \n if (flipX) {\n portX = symbol.viewBox.width - portX;\n }\n if (flipY) {\n portY = symbol.viewBox.height - portY;\n }\n\n // Apply scale (relative to symbol center)\n portX = centerX + (portX - centerX) * scale;\n portY = centerY + (portY - centerY) * scale;\n\n // Apply rotation (relative to symbol center)\n if (rotation !== 0) {\n const rad = (rotation * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n \n const dx = portX - centerX;\n const dy = portY - centerY;\n \n portX = centerX + dx * cos - dy * sin;\n portY = centerY + dx * sin + dy * cos;\n }\n\n // Translate to world position\n return {\n x: nodeX + portX,\n y: nodeY + portY,\n };\n}\n\n/**\n * Generate route points for a pipe from source port to destination port\n * \n * Options:\n * - direct: Straight line from source to destination\n * - orthogonal: Right-angle routing (future enhancement)\n * - custom: Provide your own waypoints\n */\nexport function generateRoutePoints(\n fromNode: NodeInstance,\n fromPortId: string,\n toNode: NodeInstance,\n toPortId: string,\n options?: {\n style?: 'direct' | 'orthogonal';\n waypoints?: Point[];\n }\n): Point[] {\n const startPoint = getPortWorldPosition(fromNode, fromPortId);\n const endPoint = getPortWorldPosition(toNode, toPortId);\n\n if (!startPoint || !endPoint) {\n return [];\n }\n\n const style = options?.style || 'direct';\n\n if (style === 'direct') {\n // Straight line from start to end\n return [startPoint, endPoint];\n }\n\n if (style === 'orthogonal' && options?.waypoints) {\n // Start → waypoints → end\n return [startPoint, ...options.waypoints, endPoint];\n }\n\n if (style === 'orthogonal') {\n // Simple orthogonal routing: horizontal then vertical\n const midX = (startPoint.x + endPoint.x) / 2;\n return [\n startPoint,\n { x: midX, y: startPoint.y },\n { x: midX, y: endPoint.y },\n endPoint,\n ];\n }\n\n return [startPoint, endPoint];\n}\n\n/**\n * Validate that a node has a specific port\n */\nexport function nodeHasPort(node: NodeInstance, portId: string): boolean {\n const symbol = getSymbolDefinition(node.symbolId);\n if (!symbol) return false;\n return symbol.ports.some((p) => p.id === portId);\n}\n","import React, { useState } from 'react';\nimport { getAvailableSymbols, getSymbolDefinition } from '../../diagram/symbols';\nimport type { SymbolDefinition } from '../../diagram/schema';\n\nexport interface SymbolLibraryProps {\n /** Optional className for styling */\n className?: string;\n /** Callback when a symbol drag starts */\n onSymbolDragStart?: (symbolId: string, event: React.DragEvent) => void;\n /** Show category filter */\n showCategories?: boolean;\n /** Custom symbol categories */\n categories?: string[];\n}\n\n/**\n * SymbolLibrary - Browse and drag symbols onto the canvas\n *\n * Displays available symbols organized by category\n * Supports drag-and-drop to add new nodes to diagrams\n */\nexport function SymbolLibrary({\n className = '',\n onSymbolDragStart,\n showCategories = true,\n categories = ['Valves', 'Pumps', 'Tanks', 'Instruments', 'Displays'],\n}: SymbolLibraryProps): React.ReactElement {\n const [selectedCategory, setSelectedCategory] = useState<string>('all');\n const [searchQuery, setSearchQuery] = useState('');\n\n const symbolIds = getAvailableSymbols();\n const allSymbols: SymbolDefinition[] = symbolIds\n .map((id) => getSymbolDefinition(id))\n .filter((symbol): symbol is SymbolDefinition => symbol !== undefined);\n\n // Map display category names to actual category values\n const categoryMap: Record<string, string> = {\n Valves: 'valve',\n Pumps: 'pump',\n Tanks: 'vessel',\n Instruments: 'instrument',\n Displays: 'display',\n };\n\n // Filter symbols by category and search\n const filteredSymbols = allSymbols.filter((symbol) => {\n const actualCategory = categoryMap[selectedCategory] || selectedCategory;\n const matchesCategory = selectedCategory === 'all' || symbol.category === actualCategory;\n\n const matchesSearch =\n searchQuery === '' ||\n symbol.name.toLowerCase().includes(searchQuery.toLowerCase()) ||\n symbol.id.toLowerCase().includes(searchQuery.toLowerCase());\n\n return matchesCategory && matchesSearch;\n });\n\n const handleDragStart = (symbol: SymbolDefinition, event: React.DragEvent) => {\n // Store symbol ID in drag data\n event.dataTransfer.setData('application/symbol-id', symbol.id);\n event.dataTransfer.effectAllowed = 'copy';\n onSymbolDragStart?.(symbol.id, event);\n };\n\n return (\n <div className={`symbol-library ${className}`.trim()} style={styles.container}>\n {/* Header */}\n <div style={styles.header}>\n <h3 style={styles.title}>Symbol Library</h3>\n </div>\n\n {/* Search */}\n <div style={styles.searchContainer}>\n <input\n type=\"text\"\n placeholder=\"Search symbols...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n style={styles.searchInput}\n />\n </div>\n\n {/* Category Filter */}\n {showCategories && (\n <div style={styles.categories}>\n <button\n onClick={() => setSelectedCategory('all')}\n style={{\n ...styles.categoryButton,\n ...(selectedCategory === 'all' ? styles.categoryButtonActive : {}),\n }}\n >\n All\n </button>\n {categories.map((category) => (\n <button\n key={category}\n onClick={() => setSelectedCategory(category)}\n style={{\n ...styles.categoryButton,\n ...(selectedCategory === category ? styles.categoryButtonActive : {}),\n }}\n >\n {category}\n </button>\n ))}\n </div>\n )}\n\n {/* Symbol Grid */}\n <div style={styles.symbolGrid}>\n {filteredSymbols.length === 0 ? (\n <div style={styles.emptyState}>No symbols found</div>\n ) : (\n filteredSymbols.map((symbol) => (\n <div\n key={symbol.id}\n draggable\n onDragStart={(e) => handleDragStart(symbol, e)}\n style={styles.symbolCard}\n title={symbol.metadata?.description || symbol.name}\n >\n {/* Symbol Preview */}\n <div style={styles.symbolPreview}>\n <svg\n viewBox={`${symbol.viewBox.x} ${symbol.viewBox.y} ${symbol.viewBox.width} ${symbol.viewBox.height}`}\n style={styles.symbolSvg}\n >\n <g dangerouslySetInnerHTML={{ __html: symbol.svgContent }} />\n </svg>\n </div>\n\n {/* Symbol Name */}\n <div style={styles.symbolName}>{symbol.name}</div>\n </div>\n ))\n )}\n </div>\n </div>\n );\n}\n\nconst styles: Record<string, React.CSSProperties> = {\n container: {\n display: 'flex',\n flexDirection: 'column',\n height: '100%',\n backgroundColor: '#ffffff',\n border: '1px solid #e0e0e0',\n borderRadius: '8px',\n overflow: 'hidden',\n },\n header: {\n padding: '16px',\n borderBottom: '1px solid #e0e0e0',\n backgroundColor: '#f5f5f5',\n },\n title: {\n margin: 0,\n fontSize: '16px',\n fontWeight: 600,\n color: '#263238',\n },\n searchContainer: {\n padding: '12px 16px',\n borderBottom: '1px solid #e0e0e0',\n },\n searchInput: {\n width: '100%',\n padding: '8px 12px',\n border: '1px solid #e0e0e0',\n borderRadius: '4px',\n fontSize: '14px',\n outline: 'none',\n },\n categories: {\n display: 'flex',\n gap: '8px',\n padding: '12px 16px',\n borderBottom: '1px solid #e0e0e0',\n flexWrap: 'wrap',\n },\n categoryButton: {\n padding: '6px 12px',\n border: '1px solid #e0e0e0',\n borderRadius: '4px',\n backgroundColor: '#ffffff',\n fontSize: '13px',\n cursor: 'pointer',\n transition: 'all 0.2s',\n },\n categoryButtonActive: {\n backgroundColor: '#3b82f6',\n color: '#ffffff',\n borderColor: '#3b82f6',\n },\n symbolGrid: {\n flex: 1,\n padding: '16px',\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(100px, 1fr))',\n gap: '12px',\n overflowY: 'auto',\n alignContent: 'start',\n },\n emptyState: {\n gridColumn: '1 / -1',\n textAlign: 'center',\n padding: '32px',\n color: '#999',\n },\n symbolCard: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n padding: '12px',\n border: '1px solid #e0e0e0',\n borderRadius: '6px',\n backgroundColor: '#ffffff',\n cursor: 'grab',\n transition: 'all 0.2s',\n },\n symbolPreview: {\n width: '60px',\n height: '60px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n marginBottom: '8px',\n },\n symbolSvg: {\n maxWidth: '100%',\n maxHeight: '100%',\n },\n symbolName: {\n fontSize: '12px',\n color: '#263238',\n textAlign: 'center',\n wordBreak: 'break-word',\n lineHeight: '1.3',\n },\n};\n","import React from 'react';\nimport type { NodeInstance } from '../../diagram/schema';\nimport type { TelemetryBinding } from '../../diagram/hooks/useTelemetry';\nimport {\n getAppearanceSchema,\n readAppearanceValue,\n writeAppearanceValue,\n} from '../../diagram/symbols';\nimport type { AppearanceField } from '../../diagram/symbols';\n\nexport interface NodeConfigPanelProps {\n /** Selected node to configure */\n node: NodeInstance;\n /** Telemetry binding for the node (if any) */\n binding: TelemetryBinding | null;\n /** Callback when node properties are updated */\n onUpdateNode: (updates: Partial<NodeInstance>) => void;\n /** Callback when telemetry is added/updated */\n onUpdateTelemetry: (updates: any) => void;\n /** Callback when telemetry binding is added for the first time */\n onAddTelemetry?: () => void;\n /** Callback to remove/clear the telemetry binding for this node */\n onRemoveTelemetry?: () => void;\n /** Known signal/tag names, used to autocomplete and validate 'tag' fields. */\n availableTags?: string[];\n /** Callback when node is removed */\n onRemove: () => void;\n /** Additional CSS class name */\n className?: string;\n /** Custom styles */\n style?: React.CSSProperties;\n}\n\n/**\n * NodeConfigPanel - Reusable configuration panel for editing node properties and telemetry\n *\n * @example\n * ```tsx\n * <NodeConfigPanel\n * node={selectedNode}\n * binding={telemetry.get(selectedNode.id)}\n * onUpdateNode={(updates) => updateNode(selectedNode.id, updates)}\n * onUpdateTelemetry={(updates) => updateTelemetryBatch([updates])}\n * onRemove={() => removeNode(selectedNode.id)}\n * />\n * ```\n */\nexport function NodeConfigPanel({\n node,\n binding,\n onUpdateNode,\n onUpdateTelemetry,\n onAddTelemetry,\n onRemoveTelemetry,\n availableTags = [],\n onRemove,\n className = '',\n style = {},\n}: NodeConfigPanelProps) {\n const defaultStyle: React.CSSProperties = {\n width: '320px',\n backgroundColor: '#f8fafc',\n borderLeft: '1px solid #e2e8f0',\n overflow: 'auto',\n padding: '20px',\n ...style,\n };\n\n const appearanceFields = getAppearanceSchema(node.symbolId);\n const showGenericTelemetry = appearanceFields.length === 0;\n\n return (\n <div className={className} style={defaultStyle}>\n <h2 style={{ margin: '0 0 16px 0', fontSize: '1rem', fontWeight: 600 }}>\n Configure: {node.id}\n </h2>\n\n {/* Node Settings */}\n <div style={{ marginBottom: '20px' }}>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '8px' }}>\n Node Settings\n </h3>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>Label:</label>\n <input\n type=\"text\"\n value={node.label || node.id}\n onChange={(e) => onUpdateNode({ label: e.target.value })}\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n marginBottom: '8px',\n }}\n />\n\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}>\n <div>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Label Offset X:\n </label>\n <input\n type=\"number\"\n value={node.labelOffset?.x ?? 0}\n onChange={(e) =>\n onUpdateNode({\n labelOffset: {\n x: parseFloat(e.target.value) || 0,\n y: node.labelOffset?.y ?? 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n }}\n />\n </div>\n <div>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Label Offset Y:\n </label>\n <input\n type=\"number\"\n value={node.labelOffset?.y ?? 0}\n onChange={(e) =>\n onUpdateNode({\n labelOffset: {\n x: node.labelOffset?.x ?? 0,\n y: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n }}\n />\n </div>\n </div>\n\n <div>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Label Font Size:\n </label>\n <input\n type=\"number\"\n min=\"6\"\n max=\"24\"\n value={node.labelFontSize ?? 10}\n onChange={(e) =>\n onUpdateNode({\n labelFontSize: parseFloat(e.target.value) || 10,\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n }}\n />\n </div>\n\n <div>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Rotation (degrees):\n </label>\n <input\n type=\"number\"\n min=\"0\"\n max=\"360\"\n step=\"15\"\n value={node.transform.rotation ?? 0}\n onChange={(e) =>\n onUpdateNode({\n transform: {\n ...node.transform,\n rotation: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n }}\n />\n </div>\n </div>\n\n {/* Appearance */}\n {appearanceFields.length > 0 && (\n <div style={{ marginBottom: '20px' }}>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '8px' }}>\n Appearance\n </h3>\n\n {appearanceFields.map((field: AppearanceField) => {\n const value = readAppearanceValue(node, field);\n const fieldLabel = field.label ?? field.key;\n const inputStyle = {\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n } as const;\n\n let control: React.ReactNode;\n if (field.kind === 'boolean') {\n control = (\n <label style={{ fontSize: '0.75rem', display: 'block' }}>\n <input\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={(e) => onUpdateNode(writeAppearanceValue(node, field, e.target.checked))}\n style={{ marginRight: '6px' }}\n />\n {fieldLabel}\n </label>\n );\n } else if (field.kind === 'select') {\n control = (\n <>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {fieldLabel}:\n </label>\n <select\n value={String(value)}\n onChange={(e) => onUpdateNode(writeAppearanceValue(node, field, e.target.value))}\n style={inputStyle}\n >\n {field.options?.map((opt) => (\n <option key={opt} value={opt}>\n {opt}\n </option>\n ))}\n </select>\n </>\n );\n } else if (field.kind === 'number') {\n control = (\n <>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {fieldLabel}:\n </label>\n <input\n type=\"number\"\n value={Number(value)}\n onChange={(e) =>\n onUpdateNode(writeAppearanceValue(node, field, parseFloat(e.target.value) || 0))\n }\n style={inputStyle}\n />\n </>\n );\n } else if (field.kind === 'tag') {\n const tagValue = String(value);\n const listId = `tags-${node.id}-${field.key}`;\n const known = tagValue.length > 0 && availableTags.includes(tagValue);\n const unknown = tagValue.length > 0 && !known;\n control = (\n <>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {fieldLabel}:\n </label>\n <input\n type=\"text\"\n value={tagValue}\n list={listId}\n onChange={(e) => onUpdateNode(writeAppearanceValue(node, field, e.target.value))}\n placeholder=\"type or pick a signal…\"\n style={{\n ...inputStyle,\n border: `1px solid ${unknown ? '#f59e0b' : known ? '#22c55e' : '#cbd5e1'}`,\n }}\n />\n <datalist id={listId}>\n {availableTags.map((t) => (\n <option key={t} value={t} />\n ))}\n </datalist>\n {tagValue.length > 0 && (\n <div\n style={{\n fontSize: '0.7rem',\n marginTop: '3px',\n color: known ? '#16a34a' : '#b45309',\n }}\n >\n {known\n ? '✓ matched signal'\n : '⚠ no matching signal (reads as false until published)'}\n </div>\n )}\n </>\n );\n } else {\n control = (\n <>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {fieldLabel}:\n </label>\n <input\n type=\"text\"\n value={String(value)}\n onChange={(e) => onUpdateNode(writeAppearanceValue(node, field, e.target.value))}\n style={inputStyle}\n />\n </>\n );\n }\n\n return (\n <div key={field.key} style={{ marginBottom: '10px' }}>\n {control}\n {field.help && (\n <div\n style={{\n fontSize: '0.7rem',\n color: '#64748b',\n marginTop: '3px',\n lineHeight: 1.35,\n }}\n >\n {field.help}\n </div>\n )}\n </div>\n );\n })}\n </div>\n )}\n\n {/* Telemetry Configuration */}\n {showGenericTelemetry && (\n <div style={{ marginBottom: '20px' }}>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '8px' }}>\n Telemetry Data\n </h3>\n\n {!binding ? (\n <button\n onClick={() => {\n if (onAddTelemetry) {\n onAddTelemetry();\n } else {\n // Default telemetry initialization\n onUpdateTelemetry({\n nodeId: node.id,\n tag: node.id,\n value: {\n value: 50,\n status: 'normal',\n unit: 'PSI',\n timestamp: Date.now(),\n },\n history: [45, 47, 48, 50, 52, 51, 49, 50],\n display: {\n showValue: true,\n showSparkline: false,\n precision: 1,\n backgroundColor: 'status',\n },\n thresholds: {\n highHigh: 85,\n high: 70,\n low: 30,\n lowLow: 15,\n },\n });\n }\n }}\n style={{\n padding: '8px 16px',\n backgroundColor: '#3b82f6',\n color: '#fff',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n width: '100%',\n }}\n >\n + Add Telemetry\n </button>\n ) : (\n <>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Tag (attribute):\n </label>\n <input\n type=\"text\"\n value={binding.tag || ''}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: e.target.value,\n })\n }\n placeholder=\"e.g. pump.running\"\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n marginBottom: '8px',\n }}\n />\n\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Initial Value:\n </label>\n <input\n type=\"number\"\n value={binding.value.value as number}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n value: {\n ...binding.value,\n value: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n marginBottom: '8px',\n }}\n />\n\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n Unit:\n </label>\n <input\n type=\"text\"\n value={binding.value.unit || ''}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n value: {\n ...binding.value,\n unit: e.target.value,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n marginBottom: '8px',\n }}\n />\n\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n <input\n type=\"checkbox\"\n checked={binding.display?.showSparkline ?? false}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n showSparkline: e.target.checked,\n },\n })\n }\n style={{ marginRight: '6px' }}\n />\n Show Sparkline\n </label>\n\n <div style={{ marginTop: '12px' }}>\n <label\n style={{\n fontSize: '0.75rem',\n fontWeight: 600,\n display: 'block',\n marginBottom: '8px',\n }}\n >\n Telemetry Position Offset:\n </label>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}>\n <div>\n <label style={{ fontSize: '0.7rem', display: 'block', marginBottom: '4px' }}>\n X Offset:\n </label>\n <input\n type=\"number\"\n value={binding.display?.offsetX ?? 0}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n offsetX: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '4px 6px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.75rem',\n }}\n />\n </div>\n <div>\n <label style={{ fontSize: '0.7rem', display: 'block', marginBottom: '4px' }}>\n Y Offset:\n </label>\n <input\n type=\"number\"\n value={binding.display?.offsetY ?? 0}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n offsetY: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '4px 6px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.75rem',\n }}\n />\n </div>\n </div>\n {/* Text & Sparkline Colors */}\n <div style={{ marginTop: '16px' }}>\n <label\n style={{\n fontSize: '0.75rem',\n fontWeight: 600,\n display: 'block',\n marginBottom: '8px',\n }}\n >\n Display Colors:\n </label>\n\n {/* Text Color */}\n <div style={{ marginBottom: '8px' }}>\n <label style={{ fontSize: '0.7rem', display: 'block', marginBottom: '4px' }}>\n Text Color:\n </label>\n <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>\n <input\n type=\"color\"\n value={binding.display?.textColor ?? '#000000'}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n textColor: e.target.value,\n },\n })\n }\n style={{\n width: '40px',\n height: '28px',\n border: '1px solid #cbd5e1',\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n />\n <input\n type=\"text\"\n value={binding.display?.textColor ?? '#000000'}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n textColor: e.target.value,\n },\n })\n }\n placeholder=\"#000000\"\n style={{\n flex: 1,\n padding: '4px 6px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.7rem',\n fontFamily: 'monospace',\n }}\n />\n </div>\n </div>\n\n {/* Sparkline Color */}\n {binding.display?.showSparkline && (\n <div style={{ marginBottom: '8px' }}>\n <label style={{ fontSize: '0.7rem', display: 'block', marginBottom: '4px' }}>\n Sparkline Color:\n </label>\n <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>\n <input\n type=\"color\"\n value={binding.display?.sparklineColor ?? '#3b82f6'}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n sparklineColor: e.target.value,\n },\n })\n }\n style={{\n width: '40px',\n height: '28px',\n border: '1px solid #cbd5e1',\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n />\n <input\n type=\"text\"\n value={binding.display?.sparklineColor ?? '#3b82f6'}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n sparklineColor: e.target.value,\n },\n })\n }\n placeholder=\"#3b82f6\"\n style={{\n flex: 1,\n padding: '4px 6px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.7rem',\n fontFamily: 'monospace',\n }}\n />\n </div>\n </div>\n )}\n\n {/* Background Color */}\n <div style={{ marginBottom: '8px' }}>\n <label style={{ fontSize: '0.7rem', display: 'block', marginBottom: '4px' }}>\n Background:\n </label>\n <input\n type=\"text\"\n value={binding.display?.backgroundColor ?? 'status'}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n tag: binding.tag,\n value: binding.value,\n history: binding.history,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n display: {\n ...binding.display,\n backgroundColor: e.target.value,\n },\n })\n }\n placeholder=\"status or #ffffff\"\n style={{\n width: '100%',\n padding: '4px 6px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.7rem',\n fontFamily: 'monospace',\n }}\n />\n <div style={{ fontSize: '0.65rem', color: '#6b7280', marginTop: '2px' }}>\n Use 'status' for status-based color or hex code\n </div>\n </div>\n </div>{' '}\n </div>\n\n {onRemoveTelemetry && (\n <button\n onClick={onRemoveTelemetry}\n style={{\n width: '100%',\n padding: '6px 8px',\n marginTop: '8px',\n borderRadius: '4px',\n border: '1px solid #ef4444',\n background: 'transparent',\n color: '#ef4444',\n fontSize: '0.8125rem',\n cursor: 'pointer',\n }}\n >\n Remove Telemetry\n </button>\n )}\n </>\n )}\n </div>\n )}\n\n {/* Thresholds (if telemetry exists) */}\n {showGenericTelemetry && binding && (\n <div style={{ marginBottom: '20px' }}>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '8px' }}>Thresholds</h3>\n\n {(['highHigh', 'high', 'low', 'lowLow'] as const).map((key) => {\n const label =\n key === 'highHigh'\n ? 'High-High (≥)'\n : key === 'high'\n ? 'High (≥)'\n : key === 'low'\n ? 'Low (≤)'\n : 'Low-Low (≤)';\n return (\n <div key={key} style={{ marginBottom: '8px' }}>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {label}\n </label>\n <input\n type=\"number\"\n value={\n binding.thresholds?.[key] ??\n (key === 'highHigh' ? 85 : key === 'high' ? 70 : key === 'low' ? 30 : 15)\n }\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n thresholds: {\n ...binding.thresholds,\n [key]: parseFloat(e.target.value) || 0,\n },\n })\n }\n style={{\n width: '100%',\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.875rem',\n }}\n />\n </div>\n );\n })}\n </div>\n )}\n\n {/* Status Colors (if telemetry exists) */}\n {showGenericTelemetry && binding && (\n <div style={{ marginBottom: '20px' }}>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '8px' }}>\n Status Colors\n </h3>\n\n {(['normal', 'warning', 'alarm', 'fault', 'off'] as const).map((status) => {\n const defaultColors = {\n normal: '#10b981',\n warning: '#f59e0b',\n alarm: '#ef4444',\n fault: '#8b5cf6',\n off: '#6b7280',\n };\n const label =\n status === 'normal'\n ? 'Normal'\n : status === 'warning'\n ? 'Warning'\n : status === 'alarm'\n ? 'Alarm'\n : status === 'fault'\n ? 'Fault'\n : 'Off';\n return (\n <div key={status} style={{ marginBottom: '8px' }}>\n <label style={{ fontSize: '0.75rem', display: 'block', marginBottom: '4px' }}>\n {label}:\n </label>\n <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>\n <input\n type=\"color\"\n value={binding.statusColors?.[status] ?? defaultColors[status]}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n statusColors: {\n ...binding.statusColors,\n [status]: e.target.value,\n },\n })\n }\n style={{\n width: '50px',\n height: '32px',\n border: '1px solid #cbd5e1',\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n />\n <input\n type=\"text\"\n value={binding.statusColors?.[status] ?? defaultColors[status]}\n onChange={(e) =>\n onUpdateTelemetry({\n nodeId: node.id,\n statusColors: {\n ...binding.statusColors,\n [status]: e.target.value,\n },\n })\n }\n placeholder={defaultColors[status]}\n style={{\n flex: 1,\n padding: '6px 8px',\n borderRadius: '4px',\n border: '1px solid #cbd5e1',\n fontSize: '0.75rem',\n fontFamily: 'monospace',\n }}\n />\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {/* Remove Node */}\n <div style={{ marginTop: '20px', paddingTop: '20px', borderTop: '1px solid #cbd5e1' }}>\n <button\n onClick={onRemove}\n style={{\n padding: '8px 16px',\n backgroundColor: '#ef4444',\n color: '#fff',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n width: '100%',\n }}\n >\n Remove Node\n </button>\n </div>\n </div>\n );\n}\n","import type { Diagram } from '../schema';\n\n/**\n * Validates a diagram object structure\n */\nexport function validateDiagram(diagram: unknown): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (!diagram || typeof diagram !== 'object') {\n errors.push('Diagram must be an object');\n return { valid: false, errors };\n }\n\n const d = diagram as Partial<Diagram>;\n\n // Required fields\n if (!d.id || typeof d.id !== 'string') {\n errors.push('Diagram must have a valid id (string)');\n }\n if (!d.name || typeof d.name !== 'string') {\n errors.push('Diagram must have a valid name (string)');\n }\n if (!d.version || typeof d.version !== 'string') {\n errors.push('Diagram must have a valid version (string)');\n }\n\n // ViewBox validation\n if (!d.viewBox) {\n errors.push('Diagram must have a viewBox');\n } else if (\n typeof d.viewBox.x !== 'number' ||\n typeof d.viewBox.y !== 'number' ||\n typeof d.viewBox.width !== 'number' ||\n typeof d.viewBox.height !== 'number'\n ) {\n errors.push('ViewBox must have numeric x, y, width, height properties');\n }\n\n // Array validations\n if (!Array.isArray(d.nodes)) {\n errors.push('Diagram.nodes must be an array');\n }\n if (!Array.isArray(d.pipes)) {\n errors.push('Diagram.pipes must be an array');\n }\n if (!Array.isArray(d.overlays)) {\n errors.push('Diagram.overlays must be an array');\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * Serializes a diagram to JSON string\n */\nexport function exportDiagram(diagram: Diagram): string {\n return JSON.stringify(diagram, null, 2);\n}\n\n/**\n * Parses a diagram from JSON string\n */\nexport function importDiagram(json: string): Diagram {\n try {\n const parsed = JSON.parse(json);\n const validation = validateDiagram(parsed);\n\n if (!validation.valid) {\n throw new Error(`Invalid diagram: ${validation.errors.join(', ')}`);\n }\n\n return parsed as Diagram;\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new Error(`Invalid JSON: ${error.message}`);\n }\n throw error;\n }\n}\n","import type { Diagram } from '../schema';\nimport { generateRoutePoints } from '../utils/portCalculations';\n\n// Define nodes first so we can calculate pipe routes\nconst nodes = [\n {\n id: 'V-101',\n symbolId: 'valve-ball',\n transform: {\n x: 100,\n y: 150,\n rotation: 0,\n },\n ports: [\n { id: 'inlet', x: -25, y: 0, type: 'inlet' as const },\n { id: 'outlet', x: 25, y: 0, type: 'outlet' as const },\n ],\n },\n {\n id: 'P-101',\n symbolId: 'pump-centrifugal',\n transform: {\n x: 250,\n y: 150,\n rotation: 0,\n },\n ports: [\n { id: 'inlet', x: -40, y: 0, type: 'inlet' as const },\n { id: 'outlet', x: 40, y: 0, type: 'outlet' as const },\n ],\n },\n {\n id: 'T-101',\n symbolId: 'vessel-tank',\n transform: {\n x: 450,\n y: 120,\n rotation: 0,\n },\n ports: [\n { id: 'inlet', x: 0, y: -40, type: 'inlet' as const },\n { id: 'outlet', x: 0, y: 40, type: 'outlet' as const },\n ],\n },\n];\n\n/**\n * Example P&ID diagram with a simple valve-pump-tank flow\n * Uses automatic port-to-port connection routing\n */\nexport const exampleDiagram: Diagram = {\n id: 'example-001',\n name: 'Simple Process Flow',\n version: '1.0.0',\n viewBox: {\n x: -100,\n y: -50,\n width: 1200,\n height: 600,\n },\n nodes,\n pipes: [\n {\n id: 'pipe-001',\n fromNodeId: 'V-101',\n fromPortId: 'outlet',\n toNodeId: 'P-101',\n toPortId: 'inlet',\n routePoints: generateRoutePoints(nodes[0], 'outlet', nodes[1], 'inlet'),\n style: {\n stroke: '#0277bd',\n strokeWidth: 4,\n },\n },\n {\n id: 'pipe-002',\n fromNodeId: 'P-101',\n fromPortId: 'outlet',\n toNodeId: 'T-101',\n toPortId: 'inlet',\n routePoints: generateRoutePoints(nodes[1], 'outlet', nodes[2], 'inlet', {\n style: 'orthogonal',\n }),\n style: {\n stroke: '#2e7d32',\n strokeWidth: 4,\n },\n },\n ],\n overlays: [\n {\n id: 'overlay-v101',\n type: 'value',\n anchorType: 'node',\n anchorId: 'V-101',\n offset: { x: 0, y: -40 },\n binding: {\n sourceType: 'tag',\n source: 'V101.position',\n format: '0.0%',\n },\n content: {\n text: 'Valve Position',\n },\n },\n {\n id: 'overlay-p101',\n type: 'value',\n anchorType: 'node',\n anchorId: 'P-101',\n offset: { x: 0, y: -50 },\n binding: {\n sourceType: 'tag',\n source: 'P101.flow',\n format: '0.0 LPM',\n },\n content: {\n text: 'Flow Rate',\n },\n },\n {\n id: 'overlay-t101',\n type: 'gauge',\n anchorType: 'node',\n anchorId: 'T-101',\n offset: { x: 60, y: 0 },\n binding: {\n sourceType: 'tag',\n source: 'T101.level',\n },\n content: {\n component: 'CircularGauge',\n props: {\n max: 100,\n unit: '%',\n label: 'Level',\n },\n },\n },\n ],\n metadata: {\n author: 'System',\n created: '2025-12-31T00:00:00Z',\n description: 'Example P&ID diagram demonstrating schema structure',\n tags: ['example', 'tutorial'],\n },\n};\n","/**\n * Telemetry status type\n */\nexport type TelemetryStatus = 'normal' | 'warning' | 'alarm' | 'fault' | 'off';\n\n/**\n * Threshold configuration\n */\nexport interface Thresholds {\n highHigh?: number;\n high?: number;\n low?: number;\n lowLow?: number;\n}\n\n/**\n * Calculate status based on value and thresholds\n * @param value - Current numeric value\n * @param thresholds - Threshold configuration\n * @returns Status string\n */\nexport function calculateThresholdStatus(\n value: number,\n thresholds?: Thresholds\n): TelemetryStatus {\n if (!thresholds) return 'normal';\n\n const { highHigh, high, low, lowLow } = thresholds;\n\n // Check high-high alarm (highest priority)\n if (highHigh !== undefined && value >= highHigh) {\n return 'alarm';\n }\n\n // Check low-low fault\n if (lowLow !== undefined && value <= lowLow) {\n return 'fault';\n }\n\n // Check high warning\n if (high !== undefined && value >= high) {\n return 'warning';\n }\n\n // Check low warning\n if (low !== undefined && value <= low) {\n return 'warning';\n }\n\n return 'normal';\n}\n\n/**\n * Default thresholds for typical industrial processes (percentage-based)\n */\nexport const DEFAULT_THRESHOLDS: Thresholds = {\n highHigh: 85,\n high: 70,\n low: 30,\n lowLow: 15,\n};\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { ForcedState } from './TelemetryBinding';\n\n/**\n * A custom action button rendered in the device control panel (e.g. Tare, Zero).\n */\nexport interface CustomAction {\n /** Stable action id passed to onCustomAction when clicked. */\n id: string;\n /** Button label. */\n label: string;\n /** Optional leading icon / emoji. */\n icon?: string;\n /**\n * Visual variant. The ramp variants give the button a colour that shows its\n * function:\n * - `start`: green, the same green as START (for example START RAMP).\n * - `hold`: amber (for example HOLD).\n * - `cancel`: slate grey. Red stays only on STOP, so an operator cannot\n * confuse \"cancel the ramp\" with \"stop the device\".\n * - `complete`: a status, not a command. A pale green fill, a solid green\n * border, and a check mark. The operator cannot press it, and it never\n * fires onCustomAction.\n * When `disabled` is true, `start` / `hold` / `cancel` use the same hatched,\n * dashed-border look as a disabled START.\n *\n * `primary` / `secondary` / `alarm` / `warning` are reserved and render as a\n * plain button.\n */\n variant?: 'primary' | 'secondary' | 'alarm' | 'warning' | 'start' | 'hold' | 'cancel' | 'complete';\n /**\n * When true, the button renders disabled and does NOT fire onCustomAction.\n * Use for actions that are only conditionally available (e.g. a Tare that is\n * not ready).\n */\n disabled?: boolean;\n /**\n * Tooltip shown on hover explaining why the action is unavailable. Only\n * meaningful when `disabled` is true.\n */\n disabledReason?: string;\n}\n\n/**\n * Device control capabilities and commands\n */\nexport interface ControlCapabilities {\n /**\n * Can the device be started? When `canStart` or `canStop` is true, the\n * panel shows BOTH the START and the STOP button, each in a fixed place\n * (alphinity-tffi #3853). A false `canStart` then shows START disabled.\n * It does not remove START and it does not let STOP take its place.\n */\n canStart?: boolean;\n /** Can the device be stopped? See `canStart`. */\n canStop?: boolean;\n /**\n * Does START apply now? Default: true (enabled). Set false to show START\n * disabled, for example on a pump that runs already. Use this with\n * `canStart: true` to keep \"the device has a START\" apart from \"START\n * applies now\". A disabled START does not call `onStart`.\n */\n startEnabled?: boolean;\n /**\n * Does STOP apply now? Default: true (enabled). Set false to show STOP\n * disabled, for example on a pump that is stopped. The panel itself never\n * disables STOP for a pending command. A disabled STOP does not call\n * `onStop`.\n */\n stopEnabled?: boolean;\n /** Can the device be paused? */\n canPause?: boolean;\n /** Can the device be reset? */\n canReset?: boolean;\n /** Custom actions available */\n customActions?: CustomAction[];\n}\n\n/**\n * Device control mode option\n */\nexport interface ControlMode {\n /** Mode value/ID */\n value: string;\n /** Display label */\n label: string;\n /** Description or context about this mode */\n description?: string;\n /** Whether this mode is currently available */\n enabled?: boolean;\n /** Icon or emoji to display (optional) */\n icon?: string;\n /** Mode-specific capabilities (overrides binding-level capabilities when mode is active) */\n capabilities?: ControlCapabilities;\n}\n\n/**\n * Control parameter definition (setpoint, adjustment, etc.)\n */\nexport interface ControlParameter {\n /** Parameter ID */\n id: string;\n /** Display label */\n label: string;\n /** Unit of measurement */\n unit?: string;\n /** Minimum allowed value */\n min?: number;\n /** Maximum allowed value */\n max?: number;\n /** Step increment */\n step?: number;\n /** Decimal precision for display */\n precision?: number;\n /** Current value */\n value: number | string | boolean;\n /**\n * Maintenance force state for this parameter (#3607, R19). Absent or\n * `active: false` means `value` is a real reading. See `ForcedState`.\n */\n forced?: ForcedState;\n /** Whether parameter is read-only */\n readOnly?: boolean;\n /** Parameter type */\n type?: 'number' | 'string' | 'boolean' | 'select';\n /** Options for select type */\n options?: Array<{ value: string | number; label: string }>;\n /** Custom validation function */\n validate?: (value: any) => boolean | string;\n /** Modes where this parameter is visible (if empty/undefined, visible in all modes) */\n modes?: string[];\n}\n\n/**\n * Device control state\n */\nexport interface ControlState {\n /** Current operational mode */\n currentMode: string;\n /** Is device currently running? */\n isRunning?: boolean;\n /** Is device in alarm state? */\n hasAlarm?: boolean;\n /**\n * Current status. `unknown` marks an untrusted read (OPC UA StatusCode\n * severity Bad). This library never infers `unknown`; it only displays a\n * value the backend has already published as `unknown`.\n *\n * `forced` marks a device that a maintenance force holds. It resolves to\n * `FORCED_MARK_COLOR`, the same violet as every other forced mark.\n */\n status?:\n | 'normal'\n | 'warning'\n | 'alarm'\n | 'fault'\n | 'off'\n | 'starting'\n | 'stopping'\n | 'unknown'\n | 'forced';\n /** Status message */\n statusMessage?: string;\n /** Last command timestamp */\n lastCommandTime?: number;\n /** Last command result */\n lastCommandResult?: {\n success: boolean;\n message?: string;\n };\n}\n\n/**\n * Device control binding - maps a node to device control capabilities\n *\n * Similar to TelemetryBinding but for device commands and setpoints\n */\nexport interface DeviceControlBinding {\n /** Node ID this control is bound to */\n nodeId: string;\n\n /** Device tag/ID for backend communication */\n tag: string;\n\n /** Human-readable device name */\n deviceName?: string;\n\n /** Device type (pump, valve, mixer, etc.) */\n deviceType?: string;\n\n /** Available control modes */\n modes: ControlMode[];\n\n /** Current device state */\n state: ControlState;\n\n /** Control parameters (setpoints, adjustments) organized by mode */\n parameters: Record<string, ControlParameter[]>;\n\n /** Device capabilities (what commands are available) */\n capabilities?: ControlCapabilities;\n\n /** Callbacks for control actions */\n actions: {\n /** Called when mode changes */\n onModeChange?: (mode: string) => void | Promise<void>;\n /** Called when parameter value changes */\n onParameterChange?: (parameterId: string, value: any) => void | Promise<void>;\n /** Called when start button clicked */\n onStart?: () => void | Promise<void>;\n /** Called when stop button clicked */\n onStop?: () => void | Promise<void>;\n /** Called when pause button clicked */\n onPause?: () => void | Promise<void>;\n /** Called when reset button clicked */\n onReset?: () => void | Promise<void>;\n /** Called for custom actions */\n onCustomAction?: (actionId: string) => void | Promise<void>;\n };\n\n /** Display customization */\n display?: {\n /** Panel title (defaults to deviceName or nodeId) */\n title?: string;\n /** Show mode selector */\n showModeSelector?: boolean;\n /** Show start/stop buttons */\n showStartStop?: boolean;\n /** Panel size */\n size?: 'small' | 'medium' | 'large';\n /** Custom CSS class name */\n className?: string;\n /** Custom inline styles */\n style?: React.CSSProperties;\n /** Group parameters by category */\n groupParameters?: boolean;\n /**\n * Layout of the custom action buttons.\n * - 'wrap' (default): each action has a 120px minimum width and the\n * actions wrap onto more rows when the panel is narrow.\n * - 'row': all actions share ONE row with no minimum width and a small\n * side padding, so several actions fit in a narrow faceplate. A long\n * label can wrap onto two lines inside its button.\n */\n customActionsLayout?: 'wrap' | 'row';\n };\n\n /** Styling overrides */\n theme?: {\n /** Primary color */\n primaryColor?: string;\n /** Secondary color */\n secondaryColor?: string;\n /** Accent color */\n accentColor?: string;\n /** Background color */\n backgroundColor?: string;\n /** Text color */\n textColor?: string;\n /** Status colors */\n statusColors?: {\n normal?: string;\n warning?: string;\n alarm?: string;\n fault?: string;\n off?: string;\n starting?: string;\n stopping?: string;\n unknown?: string;\n };\n };\n}\n\n/**\n * Map of device control bindings by node ID\n */\nexport type DeviceControlMap = Map<string, DeviceControlBinding>;\n\n/**\n * Helper to create a control binding from backend device configuration\n */\nexport function createControlBinding(\n nodeId: string,\n deviceConfig: {\n tag: string;\n name?: string;\n type?: string;\n modes: Array<{ value: string; label: string; enabled?: boolean }>;\n currentMode: string;\n parameters: Array<{\n id: string;\n label: string;\n unit?: string;\n min?: number;\n max?: number;\n value: any;\n modes?: string[]; // Which modes this parameter applies to\n }>;\n capabilities?: {\n canStart?: boolean;\n canStop?: boolean;\n customActions?: CustomAction[];\n };\n status?: string;\n isRunning?: boolean;\n },\n actions: DeviceControlBinding['actions']\n): DeviceControlBinding {\n // Organize parameters by mode\n const parametersByMode: Record<string, ControlParameter[]> = {};\n\n deviceConfig.parameters.forEach((param) => {\n const modes = param.modes || deviceConfig.modes.map((m) => m.value);\n\n modes.forEach((mode) => {\n if (!parametersByMode[mode]) {\n parametersByMode[mode] = [];\n }\n\n parametersByMode[mode].push({\n id: param.id,\n label: param.label,\n unit: param.unit,\n min: param.min,\n max: param.max,\n value: param.value,\n type: typeof param.value === 'number' ? 'number' : 'string',\n });\n });\n });\n\n return {\n nodeId,\n tag: deviceConfig.tag,\n deviceName: deviceConfig.name,\n deviceType: deviceConfig.type,\n modes: deviceConfig.modes,\n state: {\n currentMode: deviceConfig.currentMode,\n isRunning: deviceConfig.isRunning,\n status: deviceConfig.status as any,\n },\n parameters: parametersByMode,\n capabilities: deviceConfig.capabilities,\n actions,\n };\n}\n","import { useState, useCallback, useRef } from 'react';\nimport type { DeviceControlBinding, DeviceControlMap } from '../diagram/schema/DeviceControlBinding';\n\n/** Kinds of command a node can have in flight at once. */\nexport type CommandKind = 'start' | 'stop' | 'mode' | 'parameter' | 'action';\n\nexport interface UseDeviceControlsOptions {\n /**\n * If set (> 0), a command whose action callback does not settle within this\n * many milliseconds is abandoned: the in-flight marker clears and a timeout\n * result is recorded as command feedback. This only bounds how long the\n * optional \"sending…\" hint shows — controls are never disabled, so a hung\n * command can never lock anyone out regardless of this value.\n *\n * Note: this is a UI-level abort — it stops the HMI from *waiting*, but does\n * not cancel an already-dispatched command (that would require the action to\n * honor an AbortSignal). The command may still land; telemetry remains the\n * source of truth for the resulting device state.\n */\n commandTimeoutMs?: number;\n}\n\n/** Stable in-flight key for a (node, command) pair. */\nfunction commandKey(nodeId: string, kind: CommandKind, suffix?: string): string {\n return suffix ? `${nodeId}:${kind}:${suffix}` : `${nodeId}:${kind}`;\n}\n\n/**\n * Hook for managing device control bindings\n * Similar pattern to useTelemetry\n */\nexport function useDeviceControls(\n initialBindings?: DeviceControlBinding[],\n options?: UseDeviceControlsOptions\n) {\n const { commandTimeoutMs } = options ?? {};\n\n const [controls, setControls] = useState<DeviceControlMap>(() => {\n const map = new Map();\n if (initialBindings) {\n initialBindings.forEach((binding) => {\n map.set(binding.nodeId, binding);\n });\n }\n return map;\n });\n\n // In-flight command tracking. This is a VISIBILITY hint only — interaction\n // state, never device state, and it never gates operator agency: it does not\n // disable buttons or block commands (the HMI fails open; see the fail-open\n // principle). It exists solely so a caller can show a \"sending…\" affordance.\n // `pendingRef` is a per-key in-flight count (commands re-send freely, so the\n // same command can be in flight more than once); `pendingCommands` is the\n // reactive set of keys with a non-zero count, exposed via `isCommandPending`.\n const pendingRef = useRef<Map<string, number>>(new Map());\n const [pendingCommands, setPendingCommands] = useState<Set<string>>(() => new Set());\n\n const syncPending = useCallback(() => {\n setPendingCommands(new Set(pendingRef.current.keys()));\n }, []);\n\n /**\n * Update a specific control binding\n */\n const updateControl = useCallback((nodeId: string, updates: Partial<DeviceControlBinding>) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n \n if (existing) {\n newMap.set(nodeId, { ...existing, ...updates });\n } else {\n // If binding doesn't exist, we need at least nodeId, tag, modes, state, parameters, actions\n if ('tag' in updates && 'modes' in updates && 'state' in updates && 'parameters' in updates && 'actions' in updates) {\n newMap.set(nodeId, { nodeId, ...updates } as DeviceControlBinding);\n }\n }\n \n return newMap;\n });\n }, []);\n\n /**\n * Update multiple bindings at once\n */\n const updateControlBatch = useCallback((updates: Record<string, Partial<DeviceControlBinding>>) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n \n Object.entries(updates).forEach(([nodeId, update]) => {\n const existing = newMap.get(nodeId);\n if (existing) {\n newMap.set(nodeId, { ...existing, ...update });\n }\n });\n \n return newMap;\n });\n }, []);\n\n /**\n * Update device state (mode, status, running state, etc.)\n */\n const updateDeviceState = useCallback(\n (nodeId: string, stateUpdates: Partial<DeviceControlBinding['state']>) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n \n if (existing) {\n newMap.set(nodeId, {\n ...existing,\n state: { ...existing.state, ...stateUpdates },\n });\n }\n \n return newMap;\n });\n },\n []\n );\n\n /**\n * Update a single parameter value\n */\n const updateParameter = useCallback(\n (nodeId: string, parameterId: string, value: any) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n \n if (existing) {\n const updatedParameters = { ...existing.parameters };\n \n // Update parameter in all modes where it exists\n Object.keys(updatedParameters).forEach((mode) => {\n const paramIndex = updatedParameters[mode].findIndex((p) => p.id === parameterId);\n if (paramIndex !== -1) {\n updatedParameters[mode] = [...updatedParameters[mode]];\n updatedParameters[mode][paramIndex] = {\n ...updatedParameters[mode][paramIndex],\n value,\n };\n }\n });\n \n newMap.set(nodeId, {\n ...existing,\n parameters: updatedParameters,\n });\n }\n \n return newMap;\n });\n },\n []\n );\n\n /**\n * Set control binding (add or replace)\n */\n const setControlBinding = useCallback((binding: DeviceControlBinding) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n newMap.set(binding.nodeId, binding);\n return newMap;\n });\n }, []);\n\n /**\n * Remove control binding\n */\n const removeControlBinding = useCallback((nodeId: string) => {\n setControls((prev) => {\n const newMap = new Map(prev);\n newMap.delete(nodeId);\n return newMap;\n });\n }, []);\n\n /**\n * Get control binding for a specific node\n */\n const getControlBinding = useCallback(\n (nodeId: string): DeviceControlBinding | undefined => {\n return controls.get(nodeId);\n },\n [controls]\n );\n\n /**\n * Is there a command in flight for this node?\n *\n * Pass a `kind` to ask about a specific command (e.g. `'start'`), or omit it\n * to ask whether *any* command is pending for the node. Backed by reactive\n * state so components re-render when a command starts/settles.\n */\n const isCommandPending = useCallback(\n (nodeId: string, kind?: CommandKind): boolean => {\n if (kind) {\n const prefix = `${nodeId}:${kind}`;\n for (const key of pendingCommands) {\n if (key === prefix || key.startsWith(`${prefix}:`)) return true;\n }\n return false;\n }\n const prefix = `${nodeId}:`;\n for (const key of pendingCommands) {\n if (key.startsWith(prefix)) return true;\n }\n return false;\n },\n [pendingCommands]\n );\n\n // Increment/decrement the per-key in-flight count and publish the reactive set.\n const markPending = useCallback(\n (key: string, delta: 1 | -1) => {\n const next = (pendingRef.current.get(key) ?? 0) + delta;\n if (next > 0) pendingRef.current.set(key, next);\n else pendingRef.current.delete(key);\n syncPending();\n },\n [syncPending]\n );\n\n /**\n * Shared command runner. Every command helper routes through here so they all\n * share one contract:\n * - The HMI never authors device state. `isRunning`, `status`, `currentMode`\n * and parameter values are process truth reconciled from telemetry; a\n * command only *sends* and records its own outcome in `lastCommandResult`.\n * - Commands always dispatch — there is NO guard that blocks or de-dupes a\n * repeat press. The HMI fails open: every invocation sends. (Telemetry is\n * the source of truth, so a duplicate command is reconciled there.)\n * - The command is counted as in-flight only so a caller can show a\n * \"sending…\" hint; this never disables anything. `commandTimeoutMs` only\n * bounds how long that hint shows and records a timeout result — it cannot\n * lock a control out, because controls are never disabled.\n */\n const runCommand = useCallback(\n async (\n nodeId: string,\n key: string,\n invoke: (binding: DeviceControlBinding) => void | Promise<void>,\n messages: { success: string; failure: string }\n ): Promise<void> => {\n const binding = controls.get(nodeId);\n if (!binding) return;\n\n markPending(key, 1);\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const action = Promise.resolve(invoke(binding));\n if (commandTimeoutMs && commandTimeoutMs > 0) {\n await Promise.race([\n action,\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`${messages.success.replace(/ sent$/, '')} timed out (unconfirmed)`)),\n commandTimeoutMs\n );\n }),\n ]);\n } else {\n await action;\n }\n\n updateDeviceState(nodeId, {\n lastCommandTime: Date.now(),\n lastCommandResult: { success: true, message: messages.success },\n });\n } catch (error) {\n updateDeviceState(nodeId, {\n lastCommandTime: Date.now(),\n lastCommandResult: {\n success: false,\n message: error instanceof Error ? error.message : messages.failure,\n },\n });\n } finally {\n if (timer) clearTimeout(timer);\n markPending(key, -1);\n }\n },\n [controls, updateDeviceState, markPending, commandTimeoutMs]\n );\n\n /** Send a mode change command. Displayed mode is reconciled from telemetry. */\n const sendModeChange = useCallback(\n (nodeId: string, mode: string) =>\n runCommand(\n nodeId,\n commandKey(nodeId, 'mode'),\n (binding) => binding.actions.onModeChange?.(mode),\n { success: `Mode ${mode} command sent`, failure: 'Mode change failed' }\n ),\n [runCommand]\n );\n\n /** Send a parameter change command. Displayed value is reconciled from telemetry. */\n const sendParameterChange = useCallback(\n (nodeId: string, parameterId: string, value: any) =>\n runCommand(\n nodeId,\n commandKey(nodeId, 'parameter', parameterId),\n (binding) => binding.actions.onParameterChange?.(parameterId, value),\n { success: `Parameter ${parameterId} command sent`, failure: 'Parameter change failed' }\n ),\n [runCommand]\n );\n\n /** Send a start command. Running state is reconciled from telemetry. */\n const sendStartCommand = useCallback(\n (nodeId: string) =>\n runCommand(nodeId, commandKey(nodeId, 'start'), (binding) => binding.actions.onStart?.(), {\n success: 'Start command sent',\n failure: 'Start command failed',\n }),\n [runCommand]\n );\n\n /** Send a stop command. Running state is reconciled from telemetry. */\n const sendStopCommand = useCallback(\n (nodeId: string) =>\n runCommand(nodeId, commandKey(nodeId, 'stop'), (binding) => binding.actions.onStop?.(), {\n success: 'Stop command sent',\n failure: 'Stop command failed',\n }),\n [runCommand]\n );\n\n /** Send a custom action command. */\n const sendCustomAction = useCallback(\n (nodeId: string, actionId: string) =>\n runCommand(\n nodeId,\n commandKey(nodeId, 'action', actionId),\n (binding) => binding.actions.onCustomAction?.(actionId),\n { success: `Action ${actionId} command sent`, failure: 'Action failed' }\n ),\n [runCommand]\n );\n\n return {\n controls,\n updateControl,\n updateControlBatch,\n updateDeviceState,\n updateParameter,\n setControlBinding,\n removeControlBinding,\n getControlBinding,\n // In-flight command visibility (interaction state, not device state).\n // `isCommandPending` is the stable query surface; the raw key set is\n // intentionally not exported (its key format is an implementation detail).\n isCommandPending,\n // Command helpers\n sendModeChange,\n sendParameterChange,\n sendStartCommand,\n sendStopCommand,\n sendCustomAction,\n };\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { ForcedState } from './TelemetryBinding';\n\n/**\n * Configuration parameter definition\n */\nexport interface ConfigParameter {\n /** Parameter ID */\n id: string;\n /** Display label */\n label: string;\n /** Parameter description/help text */\n description?: string;\n /** Unit of measurement */\n unit?: string;\n /** Parameter type */\n type: 'number' | 'string' | 'boolean' | 'select' | 'multiselect';\n /** Current value */\n value: any;\n /**\n * Maintenance force state for this parameter (#3607, R19). A field the\n * operator has not overridden falls back to control's live value (the\n * pilet's own DashboardScreen does this on every config refresh), so\n * `value` here can be a live, forced reading, not only a static setting.\n * Absent or `active: false` means `value` is a real reading or a real\n * saved setting. See `ForcedState`.\n */\n forced?: ForcedState;\n /** Default value */\n defaultValue?: any;\n /** Whether parameter is read-only */\n readOnly?: boolean;\n /** Whether parameter is enabled/editable */\n enabled?: boolean;\n \n // Numeric constraints\n /** Minimum allowed value (for number type) */\n min?: number;\n /** Maximum allowed value (for number type) */\n max?: number;\n /** Step increment (for number type) */\n step?: number;\n /** Decimal precision for display (for number type) */\n precision?: number;\n \n // Selection options\n /** Options for select/multiselect type */\n options?: Array<{ value: any; label: string; description?: string }>;\n \n // Validation\n /** Custom validation function - returns true or error message */\n validate?: (value: any) => boolean | string;\n /** Required field */\n required?: boolean;\n \n // Visual grouping\n /** Section this parameter belongs to */\n section?: string;\n /** Display order within section */\n order?: number;\n \n // Dependencies\n /** Parameter IDs this depends on - if any are disabled, this is disabled */\n dependsOn?: string[];\n /** Conditional visibility - function that determines if parameter should be shown */\n visibleWhen?: (allValues: Record<string, any>) => boolean;\n}\n\n/**\n * Configuration section for grouping parameters\n */\nexport interface ConfigSection {\n /** Section ID */\n id: string;\n /** Display title */\n title: string;\n /** Section description */\n description?: string;\n /** Display order */\n order?: number;\n /** Whether section is collapsible */\n collapsible?: boolean;\n /** Default collapsed state */\n defaultCollapsed?: boolean;\n /** Icon to display */\n icon?: string;\n}\n\n/**\n * Configuration state tracking\n */\nexport interface ConfigState {\n /** Has unsaved changes */\n isDirty?: boolean;\n /** Currently saving */\n isSaving?: boolean;\n /** Last save timestamp */\n lastSaved?: number;\n /** Last save result */\n lastSaveResult?: {\n success: boolean;\n message?: string;\n };\n /** Validation errors */\n errors?: Record<string, string>;\n}\n\n/**\n * Configuration actions/handlers\n */\nexport interface ConfigActions {\n /** Handler for applying changes (temporary, may be reverted) */\n onApply?: (values: Record<string, any>) => Promise<{ success: boolean; message?: string }>;\n /** Handler for saving changes (permanent) */\n onSave?: (values: Record<string, any>) => Promise<{ success: boolean; message?: string }>;\n /** Handler for resetting to defaults */\n onReset?: () => Promise<{ success: boolean; message?: string }>;\n /** Handler for canceling changes */\n onCancel?: () => void;\n /** Handler for exporting configuration */\n onExport?: () => Promise<{ success: boolean; data?: any; message?: string }>;\n /** Handler for importing configuration */\n onImport?: (data: any) => Promise<{ success: boolean; message?: string }>;\n}\n\n/**\n * Display configuration\n */\nexport interface ConfigDisplay {\n /** Panel title */\n title?: string;\n /** Panel subtitle */\n subtitle?: string;\n /** Custom CSS class */\n className?: string;\n /** Custom inline styles */\n style?: React.CSSProperties;\n /** Show apply button */\n showApply?: boolean;\n /** Show save button */\n showSave?: boolean;\n /** Show reset button */\n showReset?: boolean;\n /** Show cancel button */\n showCancel?: boolean;\n /** Show export button */\n showExport?: boolean;\n /** Show import button */\n showImport?: boolean;\n /** Confirmation message for reset action */\n resetConfirmMessage?: string;\n /** Confirmation message for critical saves */\n saveConfirmMessage?: string;\n}\n\n/**\n * Theme customization for config panel\n */\nexport interface ConfigTheme {\n /** Background color */\n backgroundColor?: string;\n /** Surface/card color */\n surfaceColor?: string;\n /** Border color */\n borderColor?: string;\n /** Text color */\n textColor?: string;\n /** Muted text color */\n mutedTextColor?: string;\n /** Accent color */\n accentColor?: string;\n /** Error color */\n errorColor?: string;\n /** Success color */\n successColor?: string;\n /** Warning color */\n warningColor?: string;\n}\n\n/**\n * Device configuration binding - maps a node to device configuration parameters\n */\nexport interface DeviceConfigBinding {\n /** Node ID this configuration is bound to */\n nodeId: string;\n /** Device tag/identifier */\n tag: string;\n /** Device name */\n deviceName?: string;\n /** Device type */\n deviceType?: string;\n \n /** Configuration sections */\n sections?: ConfigSection[];\n /** Configuration parameters (flat list, optionally grouped by section) */\n parameters: ConfigParameter[];\n \n /** Current state */\n state: ConfigState;\n /** Available actions */\n actions: ConfigActions;\n /** Display configuration */\n display?: ConfigDisplay;\n /** Theme customization */\n theme?: ConfigTheme;\n \n /** Additional metadata */\n metadata?: Record<string, any>;\n}\n\n/**\n * Map of device configurations by node ID\n */\nexport type DeviceConfigMap = Map<string, DeviceConfigBinding>;\n\n/**\n * Helper to create a config binding with defaults\n */\nexport function createConfigBinding(\n nodeId: string,\n tag: string,\n parameters: ConfigParameter[],\n actions: ConfigActions,\n options?: {\n deviceName?: string;\n deviceType?: string;\n sections?: ConfigSection[];\n display?: ConfigDisplay;\n theme?: ConfigTheme;\n metadata?: Record<string, any>;\n }\n): DeviceConfigBinding {\n return {\n nodeId,\n tag,\n deviceName: options?.deviceName,\n deviceType: options?.deviceType,\n sections: options?.sections,\n parameters,\n state: {\n isDirty: false,\n isSaving: false,\n errors: {},\n },\n actions,\n display: options?.display,\n theme: options?.theme,\n metadata: options?.metadata,\n };\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { useState, useCallback } from 'react';\nimport type { DeviceConfigBinding, DeviceConfigMap } from '../diagram/schema/DeviceConfigBinding';\n\n/**\n * Hook for managing device configuration bindings\n */\nexport function useDeviceConfigs(initialConfigs?: DeviceConfigBinding[]) {\n const [configs, setConfigs] = useState<DeviceConfigMap>(() => {\n const map = new Map();\n if (initialConfigs) {\n initialConfigs.forEach((config) => {\n map.set(config.nodeId, config);\n });\n }\n return map;\n });\n\n /**\n * Get configuration for a specific device\n */\n const getConfig = useCallback(\n (nodeId: string): DeviceConfigBinding | undefined => {\n return configs.get(nodeId);\n },\n [configs]\n );\n\n /**\n * Update a specific configuration binding\n */\n const updateConfig = useCallback((nodeId: string, updates: Partial<DeviceConfigBinding>) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n\n if (existing) {\n newMap.set(nodeId, { ...existing, ...updates });\n } else {\n // If binding doesn't exist, create new one with required fields\n if ('tag' in updates && 'parameters' in updates && 'state' in updates && 'actions' in updates) {\n newMap.set(nodeId, { nodeId, ...updates } as DeviceConfigBinding);\n }\n }\n\n return newMap;\n });\n }, []);\n\n /**\n * Update configuration state\n */\n const updateConfigState = useCallback((nodeId: string, stateUpdates: Partial<DeviceConfigBinding['state']>) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n\n if (existing) {\n newMap.set(nodeId, {\n ...existing,\n state: { ...existing.state, ...stateUpdates },\n });\n }\n\n return newMap;\n });\n }, []);\n\n /**\n * Update multiple configurations at once\n */\n const updateConfigBatch = useCallback((updates: Record<string, Partial<DeviceConfigBinding>>) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n\n Object.entries(updates).forEach(([nodeId, update]) => {\n const existing = newMap.get(nodeId);\n if (existing) {\n newMap.set(nodeId, { ...existing, ...update });\n }\n });\n\n return newMap;\n });\n }, []);\n\n /**\n * Update parameter value\n */\n const updateParameterValue = useCallback((nodeId: string, parameterId: string, value: any) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n const existing = newMap.get(nodeId);\n\n if (existing) {\n const updatedParameters = existing.parameters.map((param) =>\n param.id === parameterId ? { ...param, value } : param\n );\n\n newMap.set(nodeId, {\n ...existing,\n parameters: updatedParameters,\n state: { ...existing.state, isDirty: true },\n });\n }\n\n return newMap;\n });\n }, []);\n\n /**\n * Apply configuration changes\n */\n const applyConfig = useCallback(\n async (nodeId: string): Promise<{ success: boolean; message?: string }> => {\n const config = configs.get(nodeId);\n if (!config || !config.actions.onApply) {\n return { success: false, message: 'No apply handler defined' };\n }\n\n // Set saving state\n updateConfigState(nodeId, { isSaving: true });\n\n try {\n // Get current values\n const values = config.parameters.reduce(\n (acc, param) => {\n acc[param.id] = param.value;\n return acc;\n },\n {} as Record<string, any>\n );\n\n // Call apply handler\n const result = await config.actions.onApply(values);\n\n // Update state with result\n updateConfigState(nodeId, {\n isSaving: false,\n lastSaveResult: result,\n isDirty: result.success ? false : config.state.isDirty,\n });\n\n return result;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Apply failed';\n updateConfigState(nodeId, {\n isSaving: false,\n lastSaveResult: { success: false, message },\n });\n return { success: false, message };\n }\n },\n [configs, updateConfigState]\n );\n\n /**\n * Save configuration changes\n */\n const saveConfig = useCallback(\n async (nodeId: string): Promise<{ success: boolean; message?: string }> => {\n const config = configs.get(nodeId);\n if (!config || !config.actions.onSave) {\n return { success: false, message: 'No save handler defined' };\n }\n\n // Set saving state\n updateConfigState(nodeId, { isSaving: true });\n\n try {\n // Get current values\n const values = config.parameters.reduce(\n (acc, param) => {\n acc[param.id] = param.value;\n return acc;\n },\n {} as Record<string, any>\n );\n\n // Call save handler\n const result = await config.actions.onSave(values);\n\n // Update state with result\n updateConfigState(nodeId, {\n isSaving: false,\n lastSaved: result.success ? Date.now() : config.state.lastSaved,\n lastSaveResult: result,\n isDirty: result.success ? false : config.state.isDirty,\n });\n\n return result;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Save failed';\n updateConfigState(nodeId, {\n isSaving: false,\n lastSaveResult: { success: false, message },\n });\n return { success: false, message };\n }\n },\n [configs, updateConfigState]\n );\n\n /**\n * Reset configuration to defaults\n */\n const resetConfig = useCallback(\n async (nodeId: string): Promise<{ success: boolean; message?: string }> => {\n const config = configs.get(nodeId);\n if (!config) {\n return { success: false, message: 'Configuration not found' };\n }\n\n try {\n // Reset to default values\n const resetParameters = config.parameters.map((param) => ({\n ...param,\n value: param.defaultValue !== undefined ? param.defaultValue : param.value,\n }));\n\n updateConfig(nodeId, {\n parameters: resetParameters,\n state: { ...config.state, isDirty: true },\n });\n\n // Call reset handler if defined\n if (config.actions.onReset) {\n const result = await config.actions.onReset();\n updateConfigState(nodeId, {\n lastSaveResult: result,\n });\n return result;\n }\n\n return { success: true, message: 'Reset to defaults' };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Reset failed';\n return { success: false, message };\n }\n },\n [configs, updateConfig, updateConfigState]\n );\n\n /**\n * Cancel configuration changes\n */\n const cancelConfig = useCallback(\n (nodeId: string) => {\n const config = configs.get(nodeId);\n if (config?.actions.onCancel) {\n config.actions.onCancel();\n }\n },\n [configs]\n );\n\n /**\n * Validate all parameters\n */\n const validateConfig = useCallback(\n (nodeId: string): { valid: boolean; errors: Record<string, string> } => {\n const config = configs.get(nodeId);\n if (!config) {\n return { valid: false, errors: { _general: 'Configuration not found' } };\n }\n\n const errors: Record<string, string> = {};\n const values = config.parameters.reduce(\n (acc, param) => {\n acc[param.id] = param.value;\n return acc;\n },\n {} as Record<string, any>\n );\n\n config.parameters.forEach((param) => {\n // Check required\n if (param.required && (param.value === undefined || param.value === null || param.value === '')) {\n errors[param.id] = `${param.label} is required`;\n return;\n }\n\n // Check numeric constraints\n if (param.type === 'number' && typeof param.value === 'number') {\n if (param.min !== undefined && param.value < param.min) {\n errors[param.id] = `${param.label} must be at least ${param.min}`;\n }\n if (param.max !== undefined && param.value > param.max) {\n errors[param.id] = `${param.label} must be at most ${param.max}`;\n }\n }\n\n // Check custom validation\n if (param.validate) {\n const result = param.validate(param.value);\n if (result !== true) {\n errors[param.id] = typeof result === 'string' ? result : `${param.label} is invalid`;\n }\n }\n\n // Check visibility\n if (param.visibleWhen && !param.visibleWhen(values)) {\n // Skip validation for hidden parameters\n delete errors[param.id];\n }\n });\n\n // Update state with errors\n updateConfigState(nodeId, { errors });\n\n return { valid: Object.keys(errors).length === 0, errors };\n },\n [configs, updateConfigState]\n );\n\n /**\n * Set configuration for a device (replaces existing)\n */\n const setConfig = useCallback((config: DeviceConfigBinding) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n newMap.set(config.nodeId, config);\n return newMap;\n });\n }, []);\n\n /**\n * Remove configuration for a device\n */\n const removeConfig = useCallback((nodeId: string) => {\n setConfigs((prev) => {\n const newMap = new Map(prev);\n newMap.delete(nodeId);\n return newMap;\n });\n }, []);\n\n /**\n * Clear all configurations\n */\n const clearConfigs = useCallback(() => {\n setConfigs(new Map());\n }, []);\n\n return {\n configs,\n getConfig,\n setConfig,\n removeConfig,\n clearConfigs,\n updateConfig,\n updateConfigState,\n updateConfigBatch,\n updateParameterValue,\n applyConfig,\n saveConfig,\n resetConfig,\n cancelConfig,\n validateConfig,\n };\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport React, { useState, useEffect, useMemo, useRef } from 'react';\nimport type {\n DeviceConfigBinding,\n ConfigParameter,\n ConfigSection,\n} from '../../diagram/schema/DeviceConfigBinding';\nimport { FORCED_MARK_COLOR } from '../../theme/statusColors';\n\nexport interface DeviceConfigPanelProps {\n /** Configuration binding to display */\n binding: DeviceConfigBinding | null;\n /** Called when panel should close */\n onClose: () => void;\n /** Called when undock button clicked (optional, shows undock button if provided) */\n onUndock?: () => void;\n /** Icon to display in undock button (default: '⇱') */\n undockIcon?: string;\n /** Panel position */\n position?: 'bottom' | 'side';\n /** Horizontal alignment for bottom position (default: center) */\n align?: 'left' | 'center' | 'right';\n /** Optimize for touch (larger targets) */\n touchOptimized?: boolean;\n /** Compact mode - reduces padding and spacing */\n compact?: boolean;\n /** Font size scale (default: 1.0) */\n fontScale?: number;\n /** Maximum height for bottom position (default: 70vh) */\n maxHeight?: string;\n /** Maximum width for bottom position (default: none) */\n maxWidth?: string;\n /** Toast notification duration in milliseconds (default: 3000) */\n toastDuration?: number;\n /** Called when controls button clicked (opens control panel for this device) */\n onOpenControls?: () => void;\n /** Show controls button in header */\n showControlsButton?: boolean;\n /**\n * Render this panel's OWN result toast. Default true, so existing\n * consumers are unchanged.\n *\n * Set false when the host application already surfaces command and save\n * results through its own notification channel. Without it the operator\n * sees two notices for one action - this panel's optimistic \"command\n * sent\", in this panel's styling, followed by the host's message for the\n * real outcome - which reads as two different mechanisms.\n *\n * The result still lands in `binding.state`, so a host that opts out can\n * read it and render its own.\n */\n showBuiltInToast?: boolean;\n /** Embedded mode - uses static positioning for rendering in containers (default: false) */\n embedded?: boolean;\n}\n\n/**\n * Memoized below (kiosk heap-leak fix): a host whose screen re-renders ~1Hz on\n * telemetry must not re-render an OPEN config panel every tick - each pass\n * re-creates the panel's handler closures and re-binds its MUI event handlers,\n * and on the kiosk those generations were RETAINED (~100 closures/sec,\n * ~2.1 MB/min heap growth -> renderer OOM at the 512 MB cap, heap-snapshot\n * fingerprint: Context/scope buckets + onSave/onReset closures). The memo only\n * pays off when the host passes identity-stable `binding` and handler props;\n * an unstable host degrades gracefully to the old behavior.\n */\nfunction DeviceConfigPanelImpl({\n binding,\n onClose,\n onUndock,\n undockIcon = '⇱',\n position = 'bottom',\n align = 'center',\n touchOptimized = true,\n compact = false,\n fontScale = 1.0,\n maxHeight,\n maxWidth,\n toastDuration = 3000,\n onOpenControls,\n showControlsButton = true,\n showBuiltInToast = true,\n embedded = false,\n}: DeviceConfigPanelProps) {\n const [localValues, setLocalValues] = useState<Record<string, any>>(() => {\n if (binding) {\n const values: Record<string, any> = {};\n binding.parameters.forEach((param) => {\n values[param.id] = param.value;\n });\n return values;\n }\n return {};\n });\n const [collapsedSections, setCollapsedSections] = useState<Set<string>>(() => {\n const collapsed = new Set<string>();\n if (binding?.sections) {\n binding.sections.forEach((section) => {\n if (section.collapsible && section.defaultCollapsed) {\n collapsed.add(section.id);\n }\n });\n }\n return collapsed;\n });\n const [showToast, setShowToast] = useState(true);\n const [bindingId, setBindingId] = useState<string | null>(binding?.nodeId || null);\n const lastResultTimestampRef = useRef<number>(0);\n\n // Reset local values when binding changes (new device selected)\n if (binding && binding.nodeId !== bindingId) {\n const values: Record<string, any> = {};\n binding.parameters.forEach((param) => {\n values[param.id] = param.value;\n });\n setLocalValues(values);\n\n // Reset collapsed sections for new binding\n const collapsed = new Set<string>();\n binding.sections?.forEach((section) => {\n if (section.collapsible && section.defaultCollapsed) {\n collapsed.add(section.id);\n }\n });\n setCollapsedSections(collapsed);\n\n setBindingId(binding.nodeId);\n }\n\n // Auto-dismiss toast - defer setState to avoid cascading renders\n useEffect(() => {\n const currentTimestamp = binding?.state.lastSaved || 0;\n if (binding?.state.lastSaveResult && currentTimestamp !== lastResultTimestampRef.current) {\n lastResultTimestampRef.current = currentTimestamp;\n\n // Defer setState to next tick to avoid cascading renders.\n // hideTimer is hoisted so the effect cleanup can clear BOTH timers — the\n // inner `return` from the setTimeout callback was discarded (setTimeout\n // ignores return values), leaving hideTimer dangling on unmount.\n let hideTimer: ReturnType<typeof setTimeout> | undefined;\n const showTimer = setTimeout(() => {\n setShowToast(true);\n hideTimer = setTimeout(() => {\n setShowToast(false);\n }, toastDuration);\n }, 0);\n\n return () => {\n clearTimeout(showTimer);\n if (hideTimer) clearTimeout(hideTimer);\n };\n }\n }, [binding?.state.lastSaveResult, binding?.state.lastSaved, toastDuration]);\n\n // Group parameters by section\n const parametersBySection = useMemo(() => {\n if (!binding) return { _unsectioned: [] };\n\n const groups: Record<string, ConfigParameter[]> = { _unsectioned: [] };\n\n binding.parameters.forEach((param) => {\n const sectionId = param.section || '_unsectioned';\n if (!groups[sectionId]) {\n groups[sectionId] = [];\n }\n groups[sectionId].push(param);\n });\n\n // Sort parameters within each section by order\n Object.keys(groups).forEach((sectionId) => {\n groups[sectionId].sort((a, b) => (a.order || 0) - (b.order || 0));\n });\n\n return groups;\n }, [binding]);\n\n // Get sections sorted by order\n const sortedSections = useMemo(() => {\n if (!binding?.sections) return [];\n return [...binding.sections].sort((a, b) => (a.order || 0) - (b.order || 0));\n }, [binding]);\n\n if (!binding) return null;\n\n const touchSize = touchOptimized ? 56 : 40;\n\n // Spacing\n const spacing = {\n padding: compact ? '12px' : '20px',\n gap: compact ? '6px' : '8px',\n marginBottom: compact ? '10px' : '16px',\n itemPadding: compact ? '8px 10px' : '10px 12px',\n };\n\n // Font sizes\n const fontSize = {\n title: Math.round(14 * fontScale),\n label: Math.round(11 * fontScale),\n value: Math.round(13 * fontScale),\n input: Math.round(13 * fontScale),\n button: Math.round(12 * fontScale),\n section: Math.round(12 * fontScale),\n description: Math.round(10 * fontScale),\n };\n\n // ISA-101 Colors\n const backgroundColor = binding.theme?.backgroundColor || '#edeeef';\n const surfaceColor = binding.theme?.surfaceColor || '#f5f6f7';\n const borderColor = binding.theme?.borderColor || '#d2d2d2';\n const textColor = binding.theme?.textColor || '#000000';\n const mutedTextColor = binding.theme?.mutedTextColor || '#666666';\n const accentColor = binding.theme?.accentColor || '#b4d0fe';\n const errorColor = binding.theme?.errorColor || '#ef4444';\n const successColor = binding.theme?.successColor || '#10b981';\n\n const handleValueChange = (parameterId: string, value: any) => {\n setLocalValues((prev) => ({ ...prev, [parameterId]: value }));\n };\n\n const handleApply = async () => {\n if (binding.actions.onApply) {\n await binding.actions.onApply(localValues);\n }\n };\n\n const handleSave = async () => {\n if (binding.actions.onSave) {\n await binding.actions.onSave(localValues);\n }\n };\n\n const handleReset = async () => {\n if (binding.display?.resetConfirmMessage) {\n if (!confirm(binding.display.resetConfirmMessage)) {\n return;\n }\n }\n if (binding.actions.onReset) {\n await binding.actions.onReset();\n }\n };\n\n const handleCancel = () => {\n if (binding.actions.onCancel) {\n binding.actions.onCancel();\n }\n onClose();\n };\n\n const toggleSection = (sectionId: string) => {\n setCollapsedSections((prev) => {\n const next = new Set(prev);\n if (next.has(sectionId)) {\n next.delete(sectionId);\n } else {\n next.add(sectionId);\n }\n return next;\n });\n };\n\n const renderParameter = (param: ConfigParameter) => {\n // Check visibility\n if (param.visibleWhen && !param.visibleWhen(localValues)) {\n return null;\n }\n\n const value = localValues[param.id];\n const error = binding.state.errors?.[param.id];\n const isDisabled = param.readOnly || param.enabled === false;\n\n return (\n <div\n key={param.id}\n style={{\n marginBottom: spacing.marginBottom,\n }}\n >\n {/* Label */}\n <label\n style={{\n display: 'block',\n fontSize: `${fontSize.label}px`,\n fontWeight: 600,\n color: textColor,\n marginBottom: '4px',\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n {param.label}\n {param.required && <span style={{ color: errorColor }}> *</span>}\n {param.unit && (\n <span style={{ color: mutedTextColor, fontWeight: 'normal' }}> ({param.unit})</span>\n )}\n {param.forced?.active && (\n <span\n aria-label=\"Forced\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '16px',\n height: '16px',\n marginLeft: '6px',\n fontSize: '10px',\n fontWeight: 700,\n textTransform: 'none',\n letterSpacing: 'normal',\n color: FORCED_MARK_COLOR.text,\n backgroundColor: FORCED_MARK_COLOR.bg,\n border: `1px solid ${FORCED_MARK_COLOR.border}`,\n borderRadius: '50%',\n verticalAlign: 'middle',\n }}\n >\n F\n </span>\n )}\n </label>\n\n {/* Real (unforced) value (#3607, R19). A parameter the operator has\n not overridden falls back to control's live value, so it can be\n a forced reading here just as much as on the control panel. */}\n {param.forced?.active && param.forced.realValue !== undefined && (\n <div\n style={{\n fontSize: `${fontSize.description}px`,\n color: mutedTextColor,\n marginTop: '-2px',\n marginBottom: '6px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n REAL{' '}\n {typeof param.forced.realValue === 'boolean'\n ? param.forced.realValue\n ? 'ON'\n : 'OFF'\n : String(param.forced.realValue)}\n {param.unit ? ` ${param.unit}` : ''}\n </div>\n )}\n\n {/* Description */}\n {param.description && (\n <div\n style={{\n fontSize: `${fontSize.description}px`,\n color: mutedTextColor,\n marginBottom: '6px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {param.description}\n </div>\n )}\n\n {/* Input */}\n {param.type === 'number' && (\n <input\n type=\"number\"\n value={value ?? ''}\n onChange={(e) => handleValueChange(param.id, parseFloat(e.target.value))}\n min={param.min}\n max={param.max}\n step={param.step}\n disabled={isDisabled}\n style={{\n width: '100%',\n height: `${touchSize * 0.8}px`,\n padding: spacing.itemPadding,\n fontSize: `${fontSize.input}px`,\n fontFamily: 'Arial, sans-serif',\n backgroundColor: isDisabled ? '#e5e7eb' : surfaceColor,\n color: textColor,\n border: `1px solid ${error ? errorColor : borderColor}`,\n borderRadius: '2px',\n outline: 'none',\n }}\n />\n )}\n\n {param.type === 'string' && (\n <input\n type=\"text\"\n value={value ?? ''}\n onChange={(e) => handleValueChange(param.id, e.target.value)}\n disabled={isDisabled}\n style={{\n width: '100%',\n height: `${touchSize * 0.8}px`,\n padding: spacing.itemPadding,\n fontSize: `${fontSize.input}px`,\n fontFamily: 'Arial, sans-serif',\n backgroundColor: isDisabled ? '#e5e7eb' : surfaceColor,\n color: textColor,\n border: `1px solid ${error ? errorColor : borderColor}`,\n borderRadius: '2px',\n outline: 'none',\n }}\n />\n )}\n\n {param.type === 'boolean' && (\n <label\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n cursor: isDisabled ? 'not-allowed' : 'pointer',\n }}\n >\n <input\n type=\"checkbox\"\n checked={value ?? false}\n onChange={(e) => handleValueChange(param.id, e.target.checked)}\n disabled={isDisabled}\n style={{\n width: '20px',\n height: '20px',\n cursor: isDisabled ? 'not-allowed' : 'pointer',\n }}\n />\n <span style={{ fontSize: `${fontSize.value}px`, color: textColor }}>\n {value ? 'Enabled' : 'Disabled'}\n </span>\n </label>\n )}\n\n {param.type === 'select' && (\n <select\n value={value ?? ''}\n onChange={(e) => handleValueChange(param.id, e.target.value)}\n disabled={isDisabled}\n style={{\n width: '100%',\n height: `${touchSize * 0.8}px`,\n padding: spacing.itemPadding,\n fontSize: `${fontSize.input}px`,\n fontFamily: 'Arial, sans-serif',\n backgroundColor: isDisabled ? '#e5e7eb' : surfaceColor,\n color: textColor,\n border: `1px solid ${error ? errorColor : borderColor}`,\n borderRadius: '2px',\n outline: 'none',\n }}\n >\n {param.options?.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n )}\n\n {/* Error message */}\n {error && (\n <div\n style={{\n fontSize: `${fontSize.description}px`,\n color: errorColor,\n marginTop: '4px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {error}\n </div>\n )}\n </div>\n );\n };\n\n const renderSection = (section: ConfigSection) => {\n const params = parametersBySection[section.id] || [];\n const isCollapsed = collapsedSections.has(section.id);\n\n return (\n <div\n key={section.id}\n style={{\n marginBottom: spacing.marginBottom,\n border: `1px solid ${borderColor}`,\n borderRadius: '4px',\n backgroundColor: surfaceColor,\n }}\n >\n {/* Section header */}\n <div\n onClick={() => section.collapsible && toggleSection(section.id)}\n style={{\n padding: spacing.itemPadding,\n borderBottom: isCollapsed ? 'none' : `1px solid ${borderColor}`,\n cursor: section.collapsible ? 'pointer' : 'default',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <div>\n <div\n style={{\n fontSize: `${fontSize.section}px`,\n fontWeight: 600,\n color: textColor,\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n {section.icon && <span style={{ marginRight: '8px' }}>{section.icon}</span>}\n {section.title}\n </div>\n {section.description && (\n <div\n style={{\n fontSize: `${fontSize.description}px`,\n color: mutedTextColor,\n marginTop: '4px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {section.description}\n </div>\n )}\n </div>\n {section.collapsible && (\n <span style={{ fontSize: '16px', color: mutedTextColor }}>\n {isCollapsed ? '▼' : '▲'}\n </span>\n )}\n </div>\n\n {/* Section content */}\n {!isCollapsed && (\n <div style={{ padding: spacing.padding }}>{params.map(renderParameter)}</div>\n )}\n </div>\n );\n };\n\n // Panel styles\n const panelStyle: React.CSSProperties = embedded\n ? {\n // Embedded mode: static positioning for container layouts\n position: 'static',\n width: '100%',\n backgroundColor,\n color: textColor,\n borderRadius: '8px 8px 0 0',\n boxShadow: 'none',\n border: `2px solid ${borderColor}`,\n padding: spacing.padding,\n maxHeight: maxHeight || '70vh',\n overflowY: 'auto',\n display: 'flex',\n flexDirection: 'column',\n ...binding.display?.style,\n }\n : {\n // Fixed mode: traditional overlay positioning\n position: 'fixed',\n ...(position === 'bottom'\n ? {\n bottom: 0,\n ...(align === 'left'\n ? { left: 0, right: 'auto', width: maxWidth }\n : align === 'right'\n ? { right: 0, left: 'auto', width: maxWidth }\n : {\n left: 0,\n right: 0,\n marginLeft: 'auto',\n marginRight: 'auto',\n maxWidth: maxWidth,\n }),\n animation: 'slideUp 0.3s ease-out',\n }\n : {\n right: 0,\n top: 0,\n bottom: 0,\n width: maxWidth || '400px',\n animation: 'slideIn 0.3s ease-out',\n }),\n backgroundColor,\n color: textColor,\n borderRadius: position === 'bottom' ? '8px 8px 0 0' : '8px 0 0 8px',\n boxShadow: 'none',\n border: `2px solid ${borderColor}`,\n padding: spacing.padding,\n zIndex: 10000,\n maxHeight: maxHeight || (position === 'bottom' ? '70vh' : '100vh'),\n overflowY: 'auto',\n display: 'flex',\n flexDirection: 'column',\n ...binding.display?.style,\n };\n\n const titleText = binding.display?.title || binding.deviceName || binding.tag || binding.nodeId;\n const showApply = binding.display?.showApply !== false && binding.actions.onApply;\n const showSave = binding.display?.showSave !== false && binding.actions.onSave;\n const showReset = binding.display?.showReset !== false && binding.actions.onReset;\n const showCancel = binding.display?.showCancel !== false;\n\n return (\n <>\n {/* Panel */}\n <div style={panelStyle} className={binding.display?.className}>\n {/* Header */}\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: '16px',\n paddingBottom: '12px',\n borderBottom: `2px solid ${borderColor}`,\n }}\n >\n <div>\n <h3\n style={{\n margin: 0,\n fontSize: `${fontSize.title}px`,\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n letterSpacing: '0.5px',\n textTransform: 'uppercase',\n color: textColor,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n maxWidth: '200px',\n }}\n title={titleText.length > 25 ? titleText : undefined}\n >\n {titleText.length > 25 ? titleText.slice(0, 25) + '...' : titleText}\n </h3>\n {binding.display?.subtitle && (\n <div\n style={{\n fontSize: `${fontSize.description}px`,\n color: mutedTextColor,\n marginTop: '4px',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n {binding.display.subtitle}\n </div>\n )}\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n {/* Controls button */}\n {showControlsButton && onOpenControls && (\n <button\n onClick={onOpenControls}\n title=\"Open Controls\"\n style={{\n minWidth: `${touchSize * 1.2}px`,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: `${fontSize.button}px`,\n cursor: 'pointer',\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n padding: '0 8px',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n CTRL\n </button>\n )}\n {/* Undock button */}\n {onUndock && (\n <button\n onClick={onUndock}\n title=\"Undock panel\"\n style={{\n width: touchSize * 0.7,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: '16px',\n cursor: 'pointer',\n fontWeight: 600,\n fontFamily: 'Arial, sans-serif',\n padding: '0',\n }}\n >\n {undockIcon}\n </button>\n )}\n {/* Close button */}\n <button\n onClick={onClose}\n style={{\n width: touchSize * 0.7,\n height: touchSize * 0.7,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: '18px',\n cursor: 'pointer',\n fontWeight: 'bold',\n fontFamily: 'Arial, sans-serif',\n }}\n >\n ×\n </button>\n </div>\n </div>\n\n {/* Dirty indicator */}\n {binding.state.isDirty && (\n <div\n style={{\n padding: spacing.itemPadding,\n backgroundColor: '#fff3cd',\n border: '1px solid #ffc107',\n borderRadius: '4px',\n marginBottom: spacing.marginBottom,\n fontSize: `${fontSize.description}px`,\n fontFamily: 'Arial, sans-serif',\n }}\n >\n Unsaved changes\n </div>\n )}\n\n {/* Content - scrollable */}\n <div style={{ flex: 1, overflowY: 'auto', marginBottom: spacing.marginBottom }}>\n {/* Render sections */}\n {sortedSections.length > 0 ? (\n sortedSections.map(renderSection)\n ) : (\n /* Render unsectioned parameters */\n <div>{parametersBySection._unsectioned?.map(renderParameter)}</div>\n )}\n </div>\n\n {/* Action buttons */}\n <div\n style={{\n display: 'flex',\n gap: spacing.gap,\n paddingTop: '12px',\n borderTop: `2px solid ${borderColor}`,\n flexWrap: 'wrap',\n }}\n >\n {showApply && (\n <button\n onClick={handleApply}\n disabled={binding.state.isSaving}\n style={{\n flex: 1,\n minWidth: '80px',\n height: `${touchSize}px`,\n backgroundColor: accentColor,\n color: '#000000',\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n cursor: binding.state.isSaving ? 'not-allowed' : 'pointer',\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n {binding.state.isSaving ? 'Applying...' : 'Apply'}\n </button>\n )}\n\n {showSave && (\n <button\n onClick={handleSave}\n disabled={binding.state.isSaving}\n style={{\n flex: 1,\n minWidth: '80px',\n height: `${touchSize}px`,\n backgroundColor: successColor,\n color: '#ffffff',\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n cursor: binding.state.isSaving ? 'not-allowed' : 'pointer',\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n {binding.state.isSaving ? 'Saving...' : 'Save'}\n </button>\n )}\n\n {showReset && (\n <button\n onClick={handleReset}\n disabled={binding.state.isSaving}\n style={{\n flex: 1,\n minWidth: '80px',\n height: `${touchSize}px`,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n cursor: binding.state.isSaving ? 'not-allowed' : 'pointer',\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n Reset\n </button>\n )}\n\n {showCancel && (\n <button\n onClick={handleCancel}\n disabled={binding.state.isSaving}\n style={{\n flex: 1,\n minWidth: '80px',\n height: `${touchSize}px`,\n backgroundColor: surfaceColor,\n color: textColor,\n border: `2px solid ${borderColor}`,\n borderRadius: '2px',\n fontSize: `${fontSize.button}px`,\n fontWeight: 600,\n cursor: binding.state.isSaving ? 'not-allowed' : 'pointer',\n fontFamily: 'Arial, sans-serif',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n }}\n >\n Cancel\n </button>\n )}\n </div>\n </div>\n\n {/* Toast notification */}\n {binding.state.lastSaveResult && showToast && showBuiltInToast && (\n <div\n style={{\n position: 'fixed',\n bottom: '20px',\n ...(align === 'right' ? { left: '20px' } : { right: '20px' }),\n backgroundColor: binding.state.lastSaveResult.success ? successColor : errorColor,\n color: '#ffffff',\n padding: '12px 20px',\n borderRadius: '4px',\n boxShadow: '0 4px 6px rgba(0,0,0,0.1)',\n zIndex: 10000,\n fontSize: `${fontSize.value}px`,\n fontFamily: 'Arial, sans-serif',\n fontWeight: 600,\n }}\n >\n {binding.state.lastSaveResult.message ||\n (binding.state.lastSaveResult.success ? 'Success' : 'Error')}\n </div>\n )}\n </>\n );\n}\n\n/** Memo boundary - see DeviceConfigPanelImpl doc comment (kiosk heap-leak fix). */\nexport const DeviceConfigPanel = React.memo(DeviceConfigPanelImpl);\n","import { useEffect } from 'react';\nimport { calculateThresholdStatus } from '../../utils/thresholds';\nimport type { TelemetryMap, TelemetryBinding } from './useTelemetry';\n\n/**\n * Options for simulation behavior\n */\nexport interface SimulationOptions {\n /** Update interval in milliseconds (default: 2000) */\n interval?: number;\n /** Whether simulation is enabled (default: true) */\n enabled?: boolean;\n /** Maximum change per update (default: 10) */\n maxChange?: number;\n /** Minimum value clamp (default: 0) */\n minValue?: number;\n /** Maximum value clamp (default: 100) */\n maxValue?: number;\n /** Maximum history length (default: 10) */\n historyLength?: number;\n /** Custom value generator function */\n generateValue?: (currentValue: number, nodeId: string) => number;\n}\n\n/**\n * Hook to automatically simulate telemetry data updates\n * Randomly updates numeric telemetry values and calculates status based on thresholds\n * \n * @param telemetry - Telemetry map from useTelemetry\n * @param updateTelemetryBatch - Batch update function from useTelemetry\n * @param options - Simulation configuration options\n * \n * @example\n * ```tsx\n * const { telemetry, updateTelemetryBatch } = useTelemetry();\n * useSimulation(telemetry, updateTelemetryBatch, {\n * interval: 2000,\n * enabled: mode === 'runtime'\n * });\n * ```\n */\nexport function useSimulation(\n telemetry: TelemetryMap,\n updateTelemetryBatch: (updates: Array<any>) => void,\n options: SimulationOptions = {}\n) {\n const {\n interval = 2000,\n enabled = true,\n maxChange = 10,\n minValue = 0,\n maxValue = 100,\n historyLength = 10,\n generateValue,\n } = options;\n\n useEffect(() => {\n if (!enabled) return;\n\n const intervalId = setInterval(() => {\n const updates: Array<{\n nodeId: string;\n tag?: string;\n value?: any;\n history?: number[];\n display?: any;\n thresholds?: any;\n statusColors?: any;\n }> = [];\n\n telemetry.forEach((binding: TelemetryBinding, nodeId: string) => {\n // Only simulate numeric values\n if (typeof binding.value.value !== 'number') return;\n\n const currentValue = binding.value.value as number;\n\n // Generate new value\n let newValue: number;\n if (generateValue) {\n newValue = generateValue(currentValue, nodeId);\n } else {\n // Default random walk simulation\n const change = (Math.random() - 0.5) * maxChange;\n newValue = Math.max(minValue, Math.min(maxValue, currentValue + change));\n }\n\n // Calculate status based on thresholds\n const status = calculateThresholdStatus(newValue, binding.thresholds);\n\n // Update history\n const newHistory = [...(binding.history || []), newValue].slice(-historyLength);\n\n updates.push({\n nodeId,\n tag: binding.tag,\n value: {\n ...binding.value,\n value: newValue,\n status,\n timestamp: Date.now(),\n },\n history: newHistory,\n display: binding.display,\n thresholds: binding.thresholds,\n statusColors: binding.statusColors,\n });\n });\n\n if (updates.length > 0) {\n updateTelemetryBatch(updates);\n }\n }, interval);\n\n return () => clearInterval(intervalId);\n }, [\n enabled,\n interval,\n maxChange,\n minValue,\n maxValue,\n historyLength,\n generateValue,\n telemetry,\n updateTelemetryBatch,\n ]);\n}\n"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAiQZ,UAAU;AAAA,MACR,aAAa;AAAA,MACb,MAAM,CAAC,UAAU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","names":["useState","jsx","selectedOption","useState","jsx","jsxs","useState","useCallback","jsx","jsxs","useState","useCallback","jsx","jsxs","useState","React","jsx","jsxs","jsx","jsxs","React","jsx","jsxs","React","useState","useEffect","Fragment","jsx","jsxs","useState","useEffect","currentParams","React","useState","useEffect","useCallback","useRef","jsx","jsxs","React","jsx","useEffect","useState","Box","styled","jsx","Fragment","jsx","jsxs","Fragment","jsx","jsxs","React","useState","useEffect","jsx","jsx","jsxs","React","useCallback","useState","useEffect","useRef","React","value","jsx","jsxs","nodes","React","React","Fragment","jsx","jsxs","React","Fragment","jsx","jsxs","React","useState","useEffect","jsx","jsxs","useState","useCallback","useRef","useEffect","useState","useCallback","useState","useCallback","useEffect","useRef","useEffect","useEffect","useEffect","useState","useCallback","useRef","useEffect","useEffect","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","nodes","React","useState","jsx","jsxs","useState","Fragment","jsx","jsxs","useState","useCallback","useRef","prefix","useState","useCallback","React","useState","useEffect","useRef","Fragment","jsx","jsxs","useState","useRef","useEffect","React","useEffect","useEffect"]}