@imperosoft/cris-webui-components 1.1.4-beta.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -64,10 +64,22 @@ interface CrisButtonProps {
64
64
  onPress?: () => void;
65
65
  /** Custom release handler */
66
66
  onRelease?: () => void;
67
+ /** Clean short press→release before holdMs (not a scroll/cancel, and not after a long-press). Mirrors PushButton ShortPushAction. */
68
+ onTap?: () => void;
69
+ /** Fires once when held past holdMs. Mirrors PushButton LargePushAction. */
70
+ onLongPress?: () => void;
71
+ /** Long-press threshold in ms (default 2000, matching PushButton LargePushTimeMs). */
72
+ holdMs?: number;
73
+ /** Fires repeatedly every repeatMs while held (ramp buttons). */
74
+ onHoldRepeat?: () => void;
75
+ /** Repeat interval in ms (default 150); first tick after one interval. */
76
+ repeatMs?: number;
77
+ /** Hold progress 0..1 toward holdMs while held; emits 0 on release/cancel. */
78
+ onHoldProgress?: (progress: number) => void;
67
79
  /** Enable debug logging for this button */
68
80
  debug?: boolean;
69
81
  }
70
- declare function CrisButton({ join, joinFeedback, joinEnable, joinVisible, text, textPressed, textSelected, icon, iconName, iconClass, iconSize, iconContainerSize, iconStyle, iconPosition, iconNameActive, iconClassActive, iconStyleActive, selected, showControlFeedback, showLocalFeedback, suppressKeyClicks, visible, enabled, smartId, className, classActive, classPressed, classDisabled, children, onPress, onRelease, debug, }: CrisButtonProps): react_jsx_runtime.JSX.Element | null;
82
+ declare function CrisButton({ join, joinFeedback, joinEnable, joinVisible, text, textPressed, textSelected, icon, iconName, iconClass, iconSize, iconContainerSize, iconStyle, iconPosition, iconNameActive, iconClassActive, iconStyleActive, selected, showControlFeedback, showLocalFeedback, suppressKeyClicks, visible, enabled, smartId, className, classActive, classPressed, classDisabled, children, onPress, onRelease, onTap, onLongPress, holdMs, onHoldRepeat, repeatMs, onHoldProgress, debug, }: CrisButtonProps): react_jsx_runtime.JSX.Element | null;
71
83
 
72
84
  interface CrisTextProps {
73
85
  /** Serial join for indirect text */
@@ -118,6 +130,15 @@ interface CrisSliderProps {
118
130
  thumbSizePercent?: number;
119
131
  /** Delay in ms after drag before updating from feedback (default 1000) */
120
132
  delayMsAfterDragUpdateFeedback?: number;
133
+ /** Controlled value (minValue..maxValue). When set, drives the slider without an analog join. */
134
+ value?: number;
135
+ /** Fired during drag with the new value, throttled to changeThrottleMs (trailing). */
136
+ onChange?: (value: number) => void;
137
+ /** Fired on drag end with the final value. */
138
+ onCommit?: (value: number) => void;
139
+ /** Min ms between onChange calls during a drag (default 100). 0 = every move. The
140
+ * thumb still moves smoothly locally; this only rate-limits the outbound onChange. */
141
+ changeThrottleMs?: number;
121
142
  /** Container CSS class */
122
143
  className?: string;
123
144
  /** Container inline style */
@@ -137,7 +158,7 @@ interface CrisSliderProps {
137
158
  /** Class when disabled */
138
159
  classDisabled?: string;
139
160
  }
140
- declare function CrisSlider({ join, joinDigital, joinAnalog, joinEnable, joinVisible, minValue, maxValue, horizontal, fillHidden, trackSizePercent, thumbSizePercent, delayMsAfterDragUpdateFeedback, className, style, barClassName, barStyle, fillClassName, fillStyle, thumbClassName, thumbStyle, classDisabled, }: CrisSliderProps): react_jsx_runtime.JSX.Element | null;
161
+ declare function CrisSlider({ join, joinDigital, joinAnalog, joinEnable, joinVisible, minValue, maxValue, horizontal, fillHidden, trackSizePercent, thumbSizePercent, delayMsAfterDragUpdateFeedback, value, onChange, onCommit, changeThrottleMs, className, style, barClassName, barStyle, fillClassName, fillStyle, thumbClassName, thumbStyle, classDisabled, }: CrisSliderProps): react_jsx_runtime.JSX.Element | null;
141
162
 
142
163
  interface CrisGaugeProps {
143
164
  /** Static value (used if no join) */
@@ -481,6 +502,278 @@ interface CrisCoMatrixListsTieProps {
481
502
  }
482
503
  declare function CrisCoMatrixListsTie({ oid, inputTitle, outputTitle, showChannels, className, style, listClassName, listStyle, headerClassName, headerStyle, itemClassName, itemStyle, itemActiveClassName, itemActiveStyle, itemDisabledClassName, vmText, vmButtonClassName, vmButtonActiveClassName, renderIoIndicator, renderSignalIndicator, }: CrisCoMatrixListsTieProps): react_jsx_runtime.JSX.Element | null;
483
504
 
505
+ type CommKind = 'none' | 'native' | 'serial' | 'ir' | 'relay' | 'tcp' | 'udp' | 'ssh' | 'http' | 'ws' | 'tcp_server' | 'ws_server' | 'custom';
506
+ /** Communication state of a device (the `cm` object of a status). */
507
+ interface DeviceComm {
508
+ /** Communication type (kind). */
509
+ kd?: CommKind;
510
+ /** Serial over ethernet. */
511
+ so?: boolean;
512
+ /** Connected (ethernet indicator state). */
513
+ co?: boolean;
514
+ /** Alive / responds (serial indicator state). */
515
+ al?: boolean;
516
+ }
517
+ /** A single indicator: whether it shows and whether it is on. */
518
+ interface CommIndicatorState {
519
+ visible: boolean;
520
+ on: boolean;
521
+ }
522
+ /** Applies the backend rule and returns the state of both indicators. */
523
+ declare function commIndicators(c?: DeviceComm): {
524
+ eth: CommIndicatorState;
525
+ serial: CommIndicatorState;
526
+ };
527
+ interface CrisViewCommClasses {
528
+ /** Outer flex row. */
529
+ root?: string;
530
+ /** Each circular dot. */
531
+ dot?: string;
532
+ /** Added to a dot when on. */
533
+ dotOn?: string;
534
+ /** Added to a dot when off. */
535
+ dotOff?: string;
536
+ /** The icon <img> inside a dot. */
537
+ icon?: string;
538
+ }
539
+ interface CrisViewCommIcons {
540
+ /** Ethernet indicator icon — name (getIconUrl) or ReactNode. Default 'ind-ethernet'. */
541
+ ethernet?: string | ReactNode;
542
+ /** Serial indicator icon — name (getIconUrl) or ReactNode. Default 'rs232'. */
543
+ serial?: string | ReactNode;
544
+ }
545
+ interface CrisViewCommProps {
546
+ comm?: DeviceComm;
547
+ classes?: CrisViewCommClasses;
548
+ icons?: CrisViewCommIcons;
549
+ /** Convenience: appended to the root slot (e.g. positioning). */
550
+ className?: string;
551
+ }
552
+ declare function CrisViewComm({ comm, classes, icons, className }: CrisViewCommProps): react_jsx_runtime.JSX.Element | null;
553
+
554
+ /**
555
+ * Slot-map for styling CrisViewDspFull and its sub-parts, plus icon config. A
556
+ * consumer overrides only the slots it cares about; every other slot keeps the
557
+ * default (which reproduces the original POC look). Final class per slot =
558
+ * `classes?.slot ?? DEFAULT.slot`.
559
+ */
560
+
561
+ interface CrisViewDspClasses {
562
+ /** Root column. */
563
+ root?: string;
564
+ /** Tab bar (relative; presets/comm are positioned within). */
565
+ header?: string;
566
+ /** Content area below the tabs. */
567
+ content?: string;
568
+ /** Tab button base. */
569
+ tab?: string;
570
+ /** Added to the selected tab. */
571
+ tabActive?: string;
572
+ /** Connecting / empty-state message. */
573
+ message?: string;
574
+ /** Presets wrapper (positioned at the left of the header). */
575
+ presetWrap?: string;
576
+ /** "Preset" caption above the buttons. */
577
+ presetLabel?: string;
578
+ /** Preset button base. */
579
+ preset?: string;
580
+ /** Added to the active preset button. */
581
+ presetActive?: string;
582
+ /** Added to a preset button while pressed. */
583
+ presetPressed?: string;
584
+ /** "Saved" flash overlay. */
585
+ savedFlash?: string;
586
+ /** Strip column. */
587
+ strip?: string;
588
+ /** Strip label. */
589
+ stripLabel?: string;
590
+ /** Level percentage readout above the fader. */
591
+ levelReadout?: string;
592
+ /** Fader bar/track. */
593
+ faderBar?: string;
594
+ /** Fader fill. */
595
+ faderFill?: string;
596
+ /** Fader thumb. */
597
+ faderThumb?: string;
598
+ /** Mute button base. */
599
+ mute?: string;
600
+ /** Added to the mute button when muted. */
601
+ muteActive?: string;
602
+ /** Mixer fixed corner. */
603
+ mixerCorner?: string;
604
+ /** Mixer sticky header/label chrome. */
605
+ mixerChrome?: string;
606
+ /** Crosspoint cell base. */
607
+ cell?: string;
608
+ /** Crosspoint cell when on. */
609
+ cellOn?: string;
610
+ /** Crosspoint cell when off. */
611
+ cellOff?: string;
612
+ }
613
+ interface CrisViewDspIcons {
614
+ /** Mixer crosspoint ON icon — name (getIconUrl) or ReactNode. Default 'audio-volume-ok'. */
615
+ crosspointOn?: string | ReactNode;
616
+ /** Mute button icon when unmuted. Default 'audio-volume-high'. */
617
+ muteOff?: string;
618
+ /** Mute button icon when muted. Default 'audio-volume-mute'. */
619
+ muteOn?: string;
620
+ /** CSS filter for the unmuted icon. Default a light-green filter. */
621
+ muteOffFilter?: string;
622
+ /** CSS filter for the muted icon. Default white. */
623
+ muteOnFilter?: string;
624
+ }
625
+
626
+ interface CrisViewDspPreset {
627
+ id: number;
628
+ name: string;
629
+ /** Which feedback field highlights this button. Default 'device'. */
630
+ kind?: 'device' | 'local';
631
+ /**
632
+ * Whether a long press saves this preset. `true` (default) → short tap recalls,
633
+ * long press saves. `false` → recall only (the long-press save gesture is not
634
+ * wired, so it cannot overwrite the preset).
635
+ */
636
+ saveAllowed?: boolean;
637
+ }
638
+
639
+ type DspTab = 'in' | 'mix' | 'out';
640
+ interface CrisViewDspFullProps {
641
+ /** OID of the DSP custom object; the view subscribes internally. */
642
+ oid: string;
643
+ /** Presets shown in the header. Omit → no presets row. */
644
+ presets?: CrisViewDspPreset[];
645
+ /** Input channel ids to hide (faders). */
646
+ skipShowInput?: number[];
647
+ /** Output channel ids to hide (faders). */
648
+ skipShowOutput?: number[];
649
+ /** Hide the whole Mixer tab. */
650
+ skipCrosspoints?: boolean;
651
+ /** Whether the active preset stays highlighted after release (default true). */
652
+ sustainedPresetFeedback?: boolean;
653
+ classes?: CrisViewDspClasses;
654
+ icons?: CrisViewDspIcons;
655
+ /** Convenience: appended to the root slot. */
656
+ className?: string;
657
+ /** Initial tab (default 'in'). */
658
+ initialTab?: DspTab;
659
+ }
660
+ declare function CrisViewDspFull({ oid, presets, skipShowInput, skipShowOutput, skipCrosspoints, sustainedPresetFeedback, classes, icons, className, initialTab, }: CrisViewDspFullProps): react_jsx_runtime.JSX.Element;
661
+
662
+ /**
663
+ * DSP custom-object protocol types (the shape exposed by a DSP backend's custom
664
+ * object, e.g. `DSP1_API` / Dsp.CrisApi.cs). The dynamic DSP view is fed entirely
665
+ * from a status of this shape — point a view at the object's OID and it renders
666
+ * whatever the DSP exposes.
667
+ */
668
+
669
+ /** Input or output channel. lv = analog level 0..65535, mt = mute. */
670
+ interface DspIo {
671
+ id: number;
672
+ lb?: string;
673
+ lv: number;
674
+ mt: boolean;
675
+ }
676
+ interface DspOut extends DspIo {
677
+ /** Crosspoints: input id → routed. In a full status only the TRUE ones arrive. */
678
+ xp?: Record<number, boolean>;
679
+ /** Crossgains: input id → gain 0..65535. Out of scope for v1. */
680
+ xg?: Record<number, number>;
681
+ }
682
+ /**
683
+ * Linked group. Confirmed against the backend (Dsp.cs `LinkedIo`): serialized as
684
+ * `lb` (label, omitted when null) and `ch` (member channels).
685
+ */
686
+ interface DspLinkedGroup {
687
+ /** Group label. Absent when the backend has it null. */
688
+ lb?: string;
689
+ /** Member channel ids. */
690
+ ch: number[];
691
+ }
692
+ interface DspLinks {
693
+ ip?: DspLinkedGroup[];
694
+ op?: DspLinkedGroup[];
695
+ }
696
+ /** Active DSP preset. dv = recovered device preset, lc = local preset. */
697
+ interface DspPreset {
698
+ dv?: number;
699
+ lc?: number;
700
+ }
701
+ /** Shape of a DSP custom-object status. */
702
+ interface DspStatus {
703
+ /** Device communication state (eth/serial indicators). */
704
+ cm?: DeviceComm;
705
+ ip: DspIo[];
706
+ op: DspOut[];
707
+ ln?: DspLinks;
708
+ pr?: DspPreset;
709
+ ps?: DspPreset;
710
+ }
711
+
712
+ /**
713
+ * Pure DSP data-model helpers. No React, no effects: they only transform a DSP
714
+ * custom-object `status` into structures ready to render.
715
+ */
716
+
717
+ /** io as the backend expects it in level.set / mute.set. */
718
+ type DspIoDir = 'in' | 'out';
719
+ /** Analog level (0..65535) → percentage 0..100 for display. */
720
+ declare function levelToPercent(lv: number): number;
721
+ /** Ensures a level falls in the valid analog range. */
722
+ declare function clampLevel(lv: number | undefined): number;
723
+ /**
724
+ * Normalizes a linked group to the UI model. Keys confirmed against the backend
725
+ * (Dsp.cs `LinkedIo`): `lb` (label) and `ch` (channels). Degrades to empty label
726
+ * / no channels when missing.
727
+ */
728
+ declare function normalizeLinked(raw: DspLinkedGroup): {
729
+ label: string;
730
+ channels: number[];
731
+ };
732
+ /** A "strip" of the Inputs/Outputs page: a loose channel or a linked group. */
733
+ type DspStrip = {
734
+ kind: 'single';
735
+ key: string;
736
+ label: string;
737
+ channels: number[];
738
+ lv: number;
739
+ mt: boolean;
740
+ } | {
741
+ kind: 'group';
742
+ key: string;
743
+ label: string;
744
+ channels: number[];
745
+ lv: number;
746
+ mt: boolean;
747
+ };
748
+ /**
749
+ * Collapses the channel list (`ip` or `op`) applying the linked groups:
750
+ * - each group shows as ONE strip; its member channels are hidden as loose ones.
751
+ * - the group state (lv/mt/label) is read from its primary member (first channel
752
+ * present in the status); if none exists, {lv:0, mt:false} is used.
753
+ * - channels that belong to no group show as loose ones, in order.
754
+ */
755
+ declare function collapseStrips(channels: DspIo[], links: DspLinkedGroup[] | undefined): DspStrip[];
756
+ /** Extracts the linked groups from the status for a given direction. */
757
+ declare function linksFor(ln: DspLinks | undefined, io: DspIoDir): DspLinkedGroup[] | undefined;
758
+ /**
759
+ * Mixer axis (one entry per channel, in order). For linked groups whose channels
760
+ * are CONSECUTIVE (and therefore adjacent in the grid) it adds the info to paint a
761
+ * two-level label: common group name (spanning) + per-member channel name (e.g.
762
+ * L/R). Loose channels or non-consecutive groups remain a single label.
763
+ */
764
+ interface MixerAxisItem {
765
+ id: number;
766
+ /** Own channel name (for groups usually "L"/"R"; for loose ones, the full name). */
767
+ channelLabel: string;
768
+ /** Common group name, present only if the channel belongs to a consecutive group. */
769
+ groupCommon?: string;
770
+ /** true on the first member of the group (paints the spanning common). */
771
+ groupStart?: boolean;
772
+ /** Number of group members (span), only on groupStart. */
773
+ groupSpan?: number;
774
+ }
775
+ declare function buildMixerAxis(channels: DspIo[], links: DspLinkedGroup[] | undefined): MixerAxisItem[];
776
+
484
777
  /**
485
778
  * Icon configuration and utilities for CRIS components
486
779
  *
@@ -531,4 +824,4 @@ declare function getIconUrl(name: string): string;
531
824
  */
532
825
  declare function getIconFilter(active: boolean): string | undefined;
533
826
 
534
- export { CrisButton, type CrisButtonProps, CrisCoDebug, type CrisCoDebugProps, CrisCoList, type CrisCoListProps, CrisCoMatrixListsTie, type CrisCoMatrixListsTieProps, CrisGauge, type CrisGaugeProps, CrisOfflinePage, type CrisOfflinePageProps, CrisSlider, type CrisSliderProps, CrisSpinner, type CrisSpinnerProps, CrisText, CrisTextInput, type CrisTextInputProps, type CrisTextProps, type DebugModule, type IconConfig, type ListItem, type ListStatus, type MatrixItem, type MatrixStatus, type SpinnerSpeed, configureIcons, getIconConfig, getIconFilter, getIconUrl };
827
+ export { type CommIndicatorState, type CommKind, CrisButton, type CrisButtonProps, CrisCoDebug, type CrisCoDebugProps, CrisCoList, type CrisCoListProps, CrisCoMatrixListsTie, type CrisCoMatrixListsTieProps, CrisGauge, type CrisGaugeProps, CrisOfflinePage, type CrisOfflinePageProps, CrisSlider, type CrisSliderProps, CrisSpinner, type CrisSpinnerProps, CrisText, CrisTextInput, type CrisTextInputProps, type CrisTextProps, CrisViewComm, type CrisViewCommClasses, type CrisViewCommIcons, type CrisViewCommProps, type CrisViewDspClasses, CrisViewDspFull, type CrisViewDspFullProps, type CrisViewDspIcons, type CrisViewDspPreset, type DebugModule, type DeviceComm, type DspIo, type DspIoDir, type DspLinkedGroup, type DspLinks, type DspOut, type DspPreset, type DspStatus, type DspStrip, type IconConfig, type ListItem, type ListStatus, type MatrixItem, type MatrixStatus, type MixerAxisItem, type SpinnerSpeed, buildMixerAxis, clampLevel, collapseStrips, commIndicators, configureIcons, getIconConfig, getIconFilter, getIconUrl, levelToPercent, linksFor, normalizeLinked };
package/dist/index.d.ts CHANGED
@@ -64,10 +64,22 @@ interface CrisButtonProps {
64
64
  onPress?: () => void;
65
65
  /** Custom release handler */
66
66
  onRelease?: () => void;
67
+ /** Clean short press→release before holdMs (not a scroll/cancel, and not after a long-press). Mirrors PushButton ShortPushAction. */
68
+ onTap?: () => void;
69
+ /** Fires once when held past holdMs. Mirrors PushButton LargePushAction. */
70
+ onLongPress?: () => void;
71
+ /** Long-press threshold in ms (default 2000, matching PushButton LargePushTimeMs). */
72
+ holdMs?: number;
73
+ /** Fires repeatedly every repeatMs while held (ramp buttons). */
74
+ onHoldRepeat?: () => void;
75
+ /** Repeat interval in ms (default 150); first tick after one interval. */
76
+ repeatMs?: number;
77
+ /** Hold progress 0..1 toward holdMs while held; emits 0 on release/cancel. */
78
+ onHoldProgress?: (progress: number) => void;
67
79
  /** Enable debug logging for this button */
68
80
  debug?: boolean;
69
81
  }
70
- declare function CrisButton({ join, joinFeedback, joinEnable, joinVisible, text, textPressed, textSelected, icon, iconName, iconClass, iconSize, iconContainerSize, iconStyle, iconPosition, iconNameActive, iconClassActive, iconStyleActive, selected, showControlFeedback, showLocalFeedback, suppressKeyClicks, visible, enabled, smartId, className, classActive, classPressed, classDisabled, children, onPress, onRelease, debug, }: CrisButtonProps): react_jsx_runtime.JSX.Element | null;
82
+ declare function CrisButton({ join, joinFeedback, joinEnable, joinVisible, text, textPressed, textSelected, icon, iconName, iconClass, iconSize, iconContainerSize, iconStyle, iconPosition, iconNameActive, iconClassActive, iconStyleActive, selected, showControlFeedback, showLocalFeedback, suppressKeyClicks, visible, enabled, smartId, className, classActive, classPressed, classDisabled, children, onPress, onRelease, onTap, onLongPress, holdMs, onHoldRepeat, repeatMs, onHoldProgress, debug, }: CrisButtonProps): react_jsx_runtime.JSX.Element | null;
71
83
 
72
84
  interface CrisTextProps {
73
85
  /** Serial join for indirect text */
@@ -118,6 +130,15 @@ interface CrisSliderProps {
118
130
  thumbSizePercent?: number;
119
131
  /** Delay in ms after drag before updating from feedback (default 1000) */
120
132
  delayMsAfterDragUpdateFeedback?: number;
133
+ /** Controlled value (minValue..maxValue). When set, drives the slider without an analog join. */
134
+ value?: number;
135
+ /** Fired during drag with the new value, throttled to changeThrottleMs (trailing). */
136
+ onChange?: (value: number) => void;
137
+ /** Fired on drag end with the final value. */
138
+ onCommit?: (value: number) => void;
139
+ /** Min ms between onChange calls during a drag (default 100). 0 = every move. The
140
+ * thumb still moves smoothly locally; this only rate-limits the outbound onChange. */
141
+ changeThrottleMs?: number;
121
142
  /** Container CSS class */
122
143
  className?: string;
123
144
  /** Container inline style */
@@ -137,7 +158,7 @@ interface CrisSliderProps {
137
158
  /** Class when disabled */
138
159
  classDisabled?: string;
139
160
  }
140
- declare function CrisSlider({ join, joinDigital, joinAnalog, joinEnable, joinVisible, minValue, maxValue, horizontal, fillHidden, trackSizePercent, thumbSizePercent, delayMsAfterDragUpdateFeedback, className, style, barClassName, barStyle, fillClassName, fillStyle, thumbClassName, thumbStyle, classDisabled, }: CrisSliderProps): react_jsx_runtime.JSX.Element | null;
161
+ declare function CrisSlider({ join, joinDigital, joinAnalog, joinEnable, joinVisible, minValue, maxValue, horizontal, fillHidden, trackSizePercent, thumbSizePercent, delayMsAfterDragUpdateFeedback, value, onChange, onCommit, changeThrottleMs, className, style, barClassName, barStyle, fillClassName, fillStyle, thumbClassName, thumbStyle, classDisabled, }: CrisSliderProps): react_jsx_runtime.JSX.Element | null;
141
162
 
142
163
  interface CrisGaugeProps {
143
164
  /** Static value (used if no join) */
@@ -481,6 +502,278 @@ interface CrisCoMatrixListsTieProps {
481
502
  }
482
503
  declare function CrisCoMatrixListsTie({ oid, inputTitle, outputTitle, showChannels, className, style, listClassName, listStyle, headerClassName, headerStyle, itemClassName, itemStyle, itemActiveClassName, itemActiveStyle, itemDisabledClassName, vmText, vmButtonClassName, vmButtonActiveClassName, renderIoIndicator, renderSignalIndicator, }: CrisCoMatrixListsTieProps): react_jsx_runtime.JSX.Element | null;
483
504
 
505
+ type CommKind = 'none' | 'native' | 'serial' | 'ir' | 'relay' | 'tcp' | 'udp' | 'ssh' | 'http' | 'ws' | 'tcp_server' | 'ws_server' | 'custom';
506
+ /** Communication state of a device (the `cm` object of a status). */
507
+ interface DeviceComm {
508
+ /** Communication type (kind). */
509
+ kd?: CommKind;
510
+ /** Serial over ethernet. */
511
+ so?: boolean;
512
+ /** Connected (ethernet indicator state). */
513
+ co?: boolean;
514
+ /** Alive / responds (serial indicator state). */
515
+ al?: boolean;
516
+ }
517
+ /** A single indicator: whether it shows and whether it is on. */
518
+ interface CommIndicatorState {
519
+ visible: boolean;
520
+ on: boolean;
521
+ }
522
+ /** Applies the backend rule and returns the state of both indicators. */
523
+ declare function commIndicators(c?: DeviceComm): {
524
+ eth: CommIndicatorState;
525
+ serial: CommIndicatorState;
526
+ };
527
+ interface CrisViewCommClasses {
528
+ /** Outer flex row. */
529
+ root?: string;
530
+ /** Each circular dot. */
531
+ dot?: string;
532
+ /** Added to a dot when on. */
533
+ dotOn?: string;
534
+ /** Added to a dot when off. */
535
+ dotOff?: string;
536
+ /** The icon <img> inside a dot. */
537
+ icon?: string;
538
+ }
539
+ interface CrisViewCommIcons {
540
+ /** Ethernet indicator icon — name (getIconUrl) or ReactNode. Default 'ind-ethernet'. */
541
+ ethernet?: string | ReactNode;
542
+ /** Serial indicator icon — name (getIconUrl) or ReactNode. Default 'rs232'. */
543
+ serial?: string | ReactNode;
544
+ }
545
+ interface CrisViewCommProps {
546
+ comm?: DeviceComm;
547
+ classes?: CrisViewCommClasses;
548
+ icons?: CrisViewCommIcons;
549
+ /** Convenience: appended to the root slot (e.g. positioning). */
550
+ className?: string;
551
+ }
552
+ declare function CrisViewComm({ comm, classes, icons, className }: CrisViewCommProps): react_jsx_runtime.JSX.Element | null;
553
+
554
+ /**
555
+ * Slot-map for styling CrisViewDspFull and its sub-parts, plus icon config. A
556
+ * consumer overrides only the slots it cares about; every other slot keeps the
557
+ * default (which reproduces the original POC look). Final class per slot =
558
+ * `classes?.slot ?? DEFAULT.slot`.
559
+ */
560
+
561
+ interface CrisViewDspClasses {
562
+ /** Root column. */
563
+ root?: string;
564
+ /** Tab bar (relative; presets/comm are positioned within). */
565
+ header?: string;
566
+ /** Content area below the tabs. */
567
+ content?: string;
568
+ /** Tab button base. */
569
+ tab?: string;
570
+ /** Added to the selected tab. */
571
+ tabActive?: string;
572
+ /** Connecting / empty-state message. */
573
+ message?: string;
574
+ /** Presets wrapper (positioned at the left of the header). */
575
+ presetWrap?: string;
576
+ /** "Preset" caption above the buttons. */
577
+ presetLabel?: string;
578
+ /** Preset button base. */
579
+ preset?: string;
580
+ /** Added to the active preset button. */
581
+ presetActive?: string;
582
+ /** Added to a preset button while pressed. */
583
+ presetPressed?: string;
584
+ /** "Saved" flash overlay. */
585
+ savedFlash?: string;
586
+ /** Strip column. */
587
+ strip?: string;
588
+ /** Strip label. */
589
+ stripLabel?: string;
590
+ /** Level percentage readout above the fader. */
591
+ levelReadout?: string;
592
+ /** Fader bar/track. */
593
+ faderBar?: string;
594
+ /** Fader fill. */
595
+ faderFill?: string;
596
+ /** Fader thumb. */
597
+ faderThumb?: string;
598
+ /** Mute button base. */
599
+ mute?: string;
600
+ /** Added to the mute button when muted. */
601
+ muteActive?: string;
602
+ /** Mixer fixed corner. */
603
+ mixerCorner?: string;
604
+ /** Mixer sticky header/label chrome. */
605
+ mixerChrome?: string;
606
+ /** Crosspoint cell base. */
607
+ cell?: string;
608
+ /** Crosspoint cell when on. */
609
+ cellOn?: string;
610
+ /** Crosspoint cell when off. */
611
+ cellOff?: string;
612
+ }
613
+ interface CrisViewDspIcons {
614
+ /** Mixer crosspoint ON icon — name (getIconUrl) or ReactNode. Default 'audio-volume-ok'. */
615
+ crosspointOn?: string | ReactNode;
616
+ /** Mute button icon when unmuted. Default 'audio-volume-high'. */
617
+ muteOff?: string;
618
+ /** Mute button icon when muted. Default 'audio-volume-mute'. */
619
+ muteOn?: string;
620
+ /** CSS filter for the unmuted icon. Default a light-green filter. */
621
+ muteOffFilter?: string;
622
+ /** CSS filter for the muted icon. Default white. */
623
+ muteOnFilter?: string;
624
+ }
625
+
626
+ interface CrisViewDspPreset {
627
+ id: number;
628
+ name: string;
629
+ /** Which feedback field highlights this button. Default 'device'. */
630
+ kind?: 'device' | 'local';
631
+ /**
632
+ * Whether a long press saves this preset. `true` (default) → short tap recalls,
633
+ * long press saves. `false` → recall only (the long-press save gesture is not
634
+ * wired, so it cannot overwrite the preset).
635
+ */
636
+ saveAllowed?: boolean;
637
+ }
638
+
639
+ type DspTab = 'in' | 'mix' | 'out';
640
+ interface CrisViewDspFullProps {
641
+ /** OID of the DSP custom object; the view subscribes internally. */
642
+ oid: string;
643
+ /** Presets shown in the header. Omit → no presets row. */
644
+ presets?: CrisViewDspPreset[];
645
+ /** Input channel ids to hide (faders). */
646
+ skipShowInput?: number[];
647
+ /** Output channel ids to hide (faders). */
648
+ skipShowOutput?: number[];
649
+ /** Hide the whole Mixer tab. */
650
+ skipCrosspoints?: boolean;
651
+ /** Whether the active preset stays highlighted after release (default true). */
652
+ sustainedPresetFeedback?: boolean;
653
+ classes?: CrisViewDspClasses;
654
+ icons?: CrisViewDspIcons;
655
+ /** Convenience: appended to the root slot. */
656
+ className?: string;
657
+ /** Initial tab (default 'in'). */
658
+ initialTab?: DspTab;
659
+ }
660
+ declare function CrisViewDspFull({ oid, presets, skipShowInput, skipShowOutput, skipCrosspoints, sustainedPresetFeedback, classes, icons, className, initialTab, }: CrisViewDspFullProps): react_jsx_runtime.JSX.Element;
661
+
662
+ /**
663
+ * DSP custom-object protocol types (the shape exposed by a DSP backend's custom
664
+ * object, e.g. `DSP1_API` / Dsp.CrisApi.cs). The dynamic DSP view is fed entirely
665
+ * from a status of this shape — point a view at the object's OID and it renders
666
+ * whatever the DSP exposes.
667
+ */
668
+
669
+ /** Input or output channel. lv = analog level 0..65535, mt = mute. */
670
+ interface DspIo {
671
+ id: number;
672
+ lb?: string;
673
+ lv: number;
674
+ mt: boolean;
675
+ }
676
+ interface DspOut extends DspIo {
677
+ /** Crosspoints: input id → routed. In a full status only the TRUE ones arrive. */
678
+ xp?: Record<number, boolean>;
679
+ /** Crossgains: input id → gain 0..65535. Out of scope for v1. */
680
+ xg?: Record<number, number>;
681
+ }
682
+ /**
683
+ * Linked group. Confirmed against the backend (Dsp.cs `LinkedIo`): serialized as
684
+ * `lb` (label, omitted when null) and `ch` (member channels).
685
+ */
686
+ interface DspLinkedGroup {
687
+ /** Group label. Absent when the backend has it null. */
688
+ lb?: string;
689
+ /** Member channel ids. */
690
+ ch: number[];
691
+ }
692
+ interface DspLinks {
693
+ ip?: DspLinkedGroup[];
694
+ op?: DspLinkedGroup[];
695
+ }
696
+ /** Active DSP preset. dv = recovered device preset, lc = local preset. */
697
+ interface DspPreset {
698
+ dv?: number;
699
+ lc?: number;
700
+ }
701
+ /** Shape of a DSP custom-object status. */
702
+ interface DspStatus {
703
+ /** Device communication state (eth/serial indicators). */
704
+ cm?: DeviceComm;
705
+ ip: DspIo[];
706
+ op: DspOut[];
707
+ ln?: DspLinks;
708
+ pr?: DspPreset;
709
+ ps?: DspPreset;
710
+ }
711
+
712
+ /**
713
+ * Pure DSP data-model helpers. No React, no effects: they only transform a DSP
714
+ * custom-object `status` into structures ready to render.
715
+ */
716
+
717
+ /** io as the backend expects it in level.set / mute.set. */
718
+ type DspIoDir = 'in' | 'out';
719
+ /** Analog level (0..65535) → percentage 0..100 for display. */
720
+ declare function levelToPercent(lv: number): number;
721
+ /** Ensures a level falls in the valid analog range. */
722
+ declare function clampLevel(lv: number | undefined): number;
723
+ /**
724
+ * Normalizes a linked group to the UI model. Keys confirmed against the backend
725
+ * (Dsp.cs `LinkedIo`): `lb` (label) and `ch` (channels). Degrades to empty label
726
+ * / no channels when missing.
727
+ */
728
+ declare function normalizeLinked(raw: DspLinkedGroup): {
729
+ label: string;
730
+ channels: number[];
731
+ };
732
+ /** A "strip" of the Inputs/Outputs page: a loose channel or a linked group. */
733
+ type DspStrip = {
734
+ kind: 'single';
735
+ key: string;
736
+ label: string;
737
+ channels: number[];
738
+ lv: number;
739
+ mt: boolean;
740
+ } | {
741
+ kind: 'group';
742
+ key: string;
743
+ label: string;
744
+ channels: number[];
745
+ lv: number;
746
+ mt: boolean;
747
+ };
748
+ /**
749
+ * Collapses the channel list (`ip` or `op`) applying the linked groups:
750
+ * - each group shows as ONE strip; its member channels are hidden as loose ones.
751
+ * - the group state (lv/mt/label) is read from its primary member (first channel
752
+ * present in the status); if none exists, {lv:0, mt:false} is used.
753
+ * - channels that belong to no group show as loose ones, in order.
754
+ */
755
+ declare function collapseStrips(channels: DspIo[], links: DspLinkedGroup[] | undefined): DspStrip[];
756
+ /** Extracts the linked groups from the status for a given direction. */
757
+ declare function linksFor(ln: DspLinks | undefined, io: DspIoDir): DspLinkedGroup[] | undefined;
758
+ /**
759
+ * Mixer axis (one entry per channel, in order). For linked groups whose channels
760
+ * are CONSECUTIVE (and therefore adjacent in the grid) it adds the info to paint a
761
+ * two-level label: common group name (spanning) + per-member channel name (e.g.
762
+ * L/R). Loose channels or non-consecutive groups remain a single label.
763
+ */
764
+ interface MixerAxisItem {
765
+ id: number;
766
+ /** Own channel name (for groups usually "L"/"R"; for loose ones, the full name). */
767
+ channelLabel: string;
768
+ /** Common group name, present only if the channel belongs to a consecutive group. */
769
+ groupCommon?: string;
770
+ /** true on the first member of the group (paints the spanning common). */
771
+ groupStart?: boolean;
772
+ /** Number of group members (span), only on groupStart. */
773
+ groupSpan?: number;
774
+ }
775
+ declare function buildMixerAxis(channels: DspIo[], links: DspLinkedGroup[] | undefined): MixerAxisItem[];
776
+
484
777
  /**
485
778
  * Icon configuration and utilities for CRIS components
486
779
  *
@@ -531,4 +824,4 @@ declare function getIconUrl(name: string): string;
531
824
  */
532
825
  declare function getIconFilter(active: boolean): string | undefined;
533
826
 
534
- export { CrisButton, type CrisButtonProps, CrisCoDebug, type CrisCoDebugProps, CrisCoList, type CrisCoListProps, CrisCoMatrixListsTie, type CrisCoMatrixListsTieProps, CrisGauge, type CrisGaugeProps, CrisOfflinePage, type CrisOfflinePageProps, CrisSlider, type CrisSliderProps, CrisSpinner, type CrisSpinnerProps, CrisText, CrisTextInput, type CrisTextInputProps, type CrisTextProps, type DebugModule, type IconConfig, type ListItem, type ListStatus, type MatrixItem, type MatrixStatus, type SpinnerSpeed, configureIcons, getIconConfig, getIconFilter, getIconUrl };
827
+ export { type CommIndicatorState, type CommKind, CrisButton, type CrisButtonProps, CrisCoDebug, type CrisCoDebugProps, CrisCoList, type CrisCoListProps, CrisCoMatrixListsTie, type CrisCoMatrixListsTieProps, CrisGauge, type CrisGaugeProps, CrisOfflinePage, type CrisOfflinePageProps, CrisSlider, type CrisSliderProps, CrisSpinner, type CrisSpinnerProps, CrisText, CrisTextInput, type CrisTextInputProps, type CrisTextProps, CrisViewComm, type CrisViewCommClasses, type CrisViewCommIcons, type CrisViewCommProps, type CrisViewDspClasses, CrisViewDspFull, type CrisViewDspFullProps, type CrisViewDspIcons, type CrisViewDspPreset, type DebugModule, type DeviceComm, type DspIo, type DspIoDir, type DspLinkedGroup, type DspLinks, type DspOut, type DspPreset, type DspStatus, type DspStrip, type IconConfig, type ListItem, type ListStatus, type MatrixItem, type MatrixStatus, type MixerAxisItem, type SpinnerSpeed, buildMixerAxis, clampLevel, collapseStrips, commIndicators, configureIcons, getIconConfig, getIconFilter, getIconUrl, levelToPercent, linksFor, normalizeLinked };