@tastic/hud 0.6.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -120,21 +120,21 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
120
120
  height: canvasSize,
121
121
  width: canvasSize
122
122
  };
123
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native13.View, { style: [styles10.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
124
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native_skia.Path, { path: unlitPath, style: "stroke", strokeWidth: UNLIT_THICKNESS, strokeCap: "round", color: mutedColor, opacity: 0.35 }),
125
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native_skia.Path, { path: litPath, style: "stroke", strokeWidth: LIT_THICKNESS, strokeCap: "round", color: accentColor, opacity: 1 })
123
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native8.View, { style: [styles7.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
124
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_skia.Path, { path: unlitPath, style: "stroke", strokeWidth: UNLIT_THICKNESS, strokeCap: "round", color: mutedColor, opacity: 0.35 }),
125
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_skia.Path, { path: litPath, style: "stroke", strokeWidth: LIT_THICKNESS, strokeCap: "round", color: accentColor, opacity: 1 })
126
126
  ] }) });
127
127
  }
128
- var import_react_native_skia, import_core4, import_react4, import_react_native13, import_react_native_reanimated, import_jsx_runtime11, UNLIT_THICKNESS, LIT_THICKNESS, DASH_GAP_PX, ARC_GAP, DEFAULT_START_DEG, MIN_DASH_ANGLE_DEG, MOUNT_DURATION_MS, MOUNT_STAGGER_MS, SELECTION_TRANSITION_DURATION_MS, styles10;
128
+ var import_react_native_skia, import_core4, import_react4, import_react_native8, import_react_native_reanimated, import_jsx_runtime8, UNLIT_THICKNESS, LIT_THICKNESS, DASH_GAP_PX, ARC_GAP, DEFAULT_START_DEG, MIN_DASH_ANGLE_DEG, MOUNT_DURATION_MS, MOUNT_STAGGER_MS, SELECTION_TRANSITION_DURATION_MS, styles7;
129
129
  var init_TriggerGauge = __esm({
130
130
  "src/TriggerGauge.tsx"() {
131
131
  "use strict";
132
132
  import_react_native_skia = require("@shopify/react-native-skia");
133
133
  import_core4 = require("@tastic/core");
134
134
  import_react4 = require("react");
135
- import_react_native13 = require("react-native");
135
+ import_react_native8 = require("react-native");
136
136
  import_react_native_reanimated = require("react-native-reanimated");
137
- import_jsx_runtime11 = require("react/jsx-runtime");
137
+ import_jsx_runtime8 = require("react/jsx-runtime");
138
138
  UNLIT_THICKNESS = 3;
139
139
  LIT_THICKNESS = 7;
140
140
  DASH_GAP_PX = 9;
@@ -144,7 +144,7 @@ var init_TriggerGauge = __esm({
144
144
  MOUNT_DURATION_MS = 450;
145
145
  MOUNT_STAGGER_MS = 40;
146
146
  SELECTION_TRANSITION_DURATION_MS = 260;
147
- styles10 = import_react_native13.StyleSheet.create({
147
+ styles7 = import_react_native8.StyleSheet.create({
148
148
  container: {
149
149
  alignItems: "center",
150
150
  justifyContent: "center",
@@ -160,8 +160,11 @@ __export(index_exports, {
160
160
  AchievementRow: () => AchievementRow,
161
161
  BaseSettingsDialog: () => BaseSettingsDialog,
162
162
  BaseStatsScreen: () => BaseStatsScreen,
163
+ ConfirmDialog: () => ConfirmDialog,
163
164
  ContentGutter: () => ContentGutter,
165
+ ControlSchemePicker: () => ControlSchemePicker,
164
166
  CornerActionButtons: () => CornerActionButtons,
167
+ CornerStatusBadges: () => CornerStatusBadges,
165
168
  InlineColorPicker: () => InlineColorPicker,
166
169
  LABELED_DROPDOWN_POPOVER_WIDTH: () => LABELED_DROPDOWN_POPOVER_WIDTH,
167
170
  LOCKED_BADGE_COLOR: () => LOCKED_BADGE_COLOR,
@@ -172,6 +175,7 @@ __export(index_exports, {
172
175
  ReadyButton: () => ReadyButton,
173
176
  SectionedDropdown: () => SectionedDropdown,
174
177
  SharedActionBand: () => SharedActionBand,
178
+ SkiaGate: () => SkiaGate,
175
179
  StatRow: () => StatRow,
176
180
  StatSection: () => StatSection,
177
181
  TriggerGaugeHost: () => TriggerGaugeHost,
@@ -262,8 +266,10 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
262
266
  function SettingIcon({ source, color, containerColor }) {
263
267
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [styles2.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Icon, { source, size: 18, color }) });
264
268
  }
265
- function BaseSettingsDialog({ visible, onDismiss, rotation = 0, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, children }) {
269
+ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, children }) {
266
270
  const { dark, colors } = (0, import_auto_paper2.useAutoPaperTheme)();
271
+ const ambientRotation = (0, import_core.useRotation)();
272
+ const rotation = rotationOverride ?? ambientRotation;
267
273
  const isTouchPrimary = (0, import_core.useIsTouchPrimaryDevice)();
268
274
  const { settings: hapticSettings, set: setHapticSettings } = (0, import_feedback_press.useHapticSettings)();
269
275
  const { settings: soundSettings, set: setSoundSettings } = (0, import_feedback_press.useSoundSettings)();
@@ -455,12 +461,15 @@ var styles2 = import_react_native2.StyleSheet.create({
455
461
  // src/BaseStatsScreen.tsx
456
462
  var import_auto_paper3 = require("@rific/auto-paper");
457
463
  var import_feedback_press2 = require("@rific/feedback-press");
464
+ var import_core2 = require("@tastic/core");
458
465
  var import_react2 = require("react");
459
466
  var import_react_native3 = require("react-native");
460
467
  var import_react_native_paper3 = require("react-native-paper");
461
468
  var import_jsx_runtime3 = require("react/jsx-runtime");
462
- function BaseStatsScreen({ rotation = 0, title = "Achievements", titleVariant = "headlineSmall", onBack, insets, onReset, resetLabel = "Reset All Stats", resetConfirmTitle = "Reset Everything?", resetConfirmBody = "This permanently erases all stats and achievements. This cannot be undone.", fg: fgOverride, bg: bgOverride, cardBg: cardBgOverride, accentColor: accentColorOverride, children }) {
469
+ function BaseStatsScreen({ rotation: rotationOverride, title = "Achievements", titleVariant = "headlineSmall", onBack, insets, onReset, resetLabel = "Reset All Stats", resetConfirmTitle = "Reset Everything?", resetConfirmBody = "This permanently erases all stats and achievements. This cannot be undone.", fg: fgOverride, bg: bgOverride, cardBg: cardBgOverride, accentColor: accentColorOverride, children }) {
463
470
  const { dark, colors } = (0, import_auto_paper3.useAutoPaperTheme)();
471
+ const ambientRotation = (0, import_core2.useRotation)();
472
+ const rotation = rotationOverride ?? ambientRotation;
464
473
  const [confirmResetVisible, setConfirmResetVisible] = (0, import_react2.useState)(false);
465
474
  const showReset = !!onReset;
466
475
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
@@ -553,60 +562,104 @@ var styles3 = import_react_native3.StyleSheet.create({
553
562
  }
554
563
  });
555
564
 
556
- // src/ContentGutter.tsx
557
- var import_core2 = require("@tastic/core");
565
+ // src/ConfirmDialog.tsx
566
+ var import_auto_paper4 = require("@rific/auto-paper");
567
+ var import_feedback_press3 = require("@rific/feedback-press");
558
568
  var import_react_native4 = require("react-native");
569
+ var import_react_native_paper4 = require("react-native-paper");
559
570
  var import_jsx_runtime4 = require("react/jsx-runtime");
560
- function ContentGutter({ maxContentWidth, leftGutter, rightGutter, style, children }) {
561
- const { width: windowWidth } = (0, import_react_native4.useWindowDimensions)();
562
- const { contentWidth, gutterWidth } = (0, import_core2.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
563
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: [styles4.row, style], children: [
564
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: [styles4.gutter, { width: gutterWidth }], children: leftGutter }),
565
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: { width: contentWidth }, children }),
566
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: [styles4.gutter, { width: gutterWidth }], children: rightGutter })
567
- ] });
571
+ function ConfirmDialog({ visible, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, rotation = 0, icon = "help-circle-outline", destructive = false }) {
572
+ const { dark, colors } = (0, import_auto_paper4.useAutoPaperTheme)();
573
+ if (!visible) return null;
574
+ const fg = dark ? "#FFFFFF" : "#000000";
575
+ const cardBg = dark ? "#111111" : "#F2F2F2";
576
+ const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
577
+ const accent = destructive ? colors.danger : colors.primary;
578
+ const onAccent = destructive ? colors.onDanger : colors.onPrimary;
579
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: styles4.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: [styles4.card, { backgroundColor: cardBg, borderColor: cardBorder }, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
580
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Icon, { source: icon, size: 64, color: accent }),
581
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Text, { variant: "headlineLarge", style: [styles4.title, { color: accent }], children: title }),
582
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Text, { variant: "bodyLarge", style: [styles4.body, { color: fg }], children: message }),
583
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles4.actions, children: [
584
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press3.Button, { mode: "outlined", onPress: onCancel, style: styles4.actionButton, children: cancelLabel }),
585
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press3.Button, { mode: "contained", onPress: onConfirm, style: styles4.actionButton, buttonColor: accent, textColor: onAccent, children: confirmLabel })
586
+ ] })
587
+ ] }) }) });
568
588
  }
569
589
  var styles4 = import_react_native4.StyleSheet.create({
570
- // overflow:'hidden' keeps an oversized leftGutter/rightGutter from bleeding into the content
571
- // column instead of clipping cleanly at its own real width.
572
- gutter: {
573
- overflow: "hidden"
590
+ actionButton: {
591
+ flex: 1
574
592
  },
575
- row: {
576
- flexDirection: "row"
593
+ actions: {
594
+ flexDirection: "row",
595
+ gap: 12,
596
+ width: "100%"
597
+ },
598
+ body: {
599
+ textAlign: "center"
600
+ },
601
+ card: {
602
+ alignItems: "center",
603
+ borderRadius: 20,
604
+ borderWidth: 1,
605
+ gap: 16,
606
+ maxWidth: 360,
607
+ padding: 32
608
+ },
609
+ overlay: {
610
+ alignItems: "center",
611
+ backgroundColor: "rgba(0,0,0,0.72)",
612
+ bottom: 0,
613
+ justifyContent: "center",
614
+ left: 0,
615
+ position: "absolute",
616
+ right: 0,
617
+ top: 0
618
+ },
619
+ title: {
620
+ fontWeight: "bold"
577
621
  }
578
622
  });
579
623
 
580
- // src/CornerActionButtons.tsx
581
- var import_feedback_press3 = require("@rific/feedback-press");
624
+ // src/ContentGutter.tsx
625
+ var import_core3 = require("@tastic/core");
582
626
  var import_react_native5 = require("react-native");
583
627
  var import_jsx_runtime5 = require("react/jsx-runtime");
584
- function CornerActionButtons({ onBack, onSettings, fg, insets }) {
585
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
586
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, style: [styles5.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: "Back" }),
587
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_feedback_press3.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles5.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
628
+ function ContentGutter({ maxContentWidth, leftGutter, rightGutter, style, children }) {
629
+ const { width: windowWidth } = (0, import_react_native5.useWindowDimensions)();
630
+ const { contentWidth, gutterWidth } = (0, import_core3.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
631
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: [styles5.row, style], children: [
632
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: [styles5.gutter, { width: gutterWidth }], children: leftGutter }),
633
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: { width: contentWidth }, children }),
634
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: [styles5.gutter, { width: gutterWidth }], children: rightGutter })
588
635
  ] });
589
636
  }
590
637
  var styles5 = import_react_native5.StyleSheet.create({
591
- back: {
592
- position: "absolute"
638
+ // overflow:'hidden' keeps an oversized leftGutter/rightGutter from bleeding into the content
639
+ // column instead of clipping cleanly at its own real width.
640
+ gutter: {
641
+ overflow: "hidden"
593
642
  },
594
- topRight: {
595
- position: "absolute"
643
+ row: {
644
+ flexDirection: "row"
596
645
  }
597
646
  });
598
647
 
648
+ // src/ControlSchemePicker.tsx
649
+ var import_core6 = require("@tastic/core");
650
+ var import_react_native11 = require("react-native");
651
+
652
+ // src/SectionedDropdown.tsx
653
+ var import_auto_paper5 = require("@rific/auto-paper");
654
+ var import_feedback_press4 = require("@rific/feedback-press");
655
+ var import_core5 = require("@tastic/core");
656
+ var import_react_native10 = require("react-native");
657
+ var import_react_native_paper5 = require("react-native-paper");
658
+
599
659
  // src/fonts.ts
600
660
  var import_react_native6 = require("react-native");
601
661
  var MONO_FONT = import_react_native6.Platform.select({ ios: "Menlo", android: "monospace", default: "monospace" });
602
662
 
603
- // src/InlineColorPicker.tsx
604
- var import_auto_paper4 = require("@rific/auto-paper");
605
- var import_feedback_press4 = require("@rific/feedback-press");
606
- var import_core3 = require("@tastic/core");
607
- var import_react_native9 = require("react-native");
608
- var import_react_native_paper4 = require("react-native-paper");
609
-
610
663
  // src/PopoverBody.tsx
611
664
  var import_react_native7 = require("react-native");
612
665
  var import_jsx_runtime6 = require("react/jsx-runtime");
@@ -673,24 +726,59 @@ var styles6 = import_react_native7.StyleSheet.create({
673
726
  }
674
727
  });
675
728
 
676
- // src/useAutoAlign.ts
729
+ // src/SkiaGate.tsx
677
730
  var import_react3 = require("react");
678
- var import_react_native8 = require("react-native");
731
+ var import_jsx_runtime7 = require("react/jsx-runtime");
732
+ function SkiaGate({ getComponent, componentProps, fallback = null }) {
733
+ const [Component, setComponent] = (0, import_react3.useState)(null);
734
+ (0, import_react3.useEffect)(() => {
735
+ let cancelled = false;
736
+ getComponent().then((mod) => {
737
+ if (!cancelled) setComponent(() => mod.default);
738
+ });
739
+ return () => {
740
+ cancelled = true;
741
+ };
742
+ }, [getComponent]);
743
+ if (!Component) return fallback;
744
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Component, { ...componentProps });
745
+ }
746
+
747
+ // src/TriggerGaugeHost.tsx
748
+ var import_jsx_runtime9 = require("react/jsx-runtime");
749
+ var getTriggerGauge = () => Promise.resolve().then(() => (init_TriggerGauge(), TriggerGauge_exports)).then((mod) => ({ default: mod.TriggerGauge }));
750
+ function TriggerGaugeHost(props) {
751
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
752
+ }
753
+
754
+ // src/useAutoAlign.ts
755
+ var import_react5 = require("react");
756
+ var import_react_native9 = require("react-native");
679
757
  var EDGE_MARGIN = 12;
680
- function useAutoAlign(open, contentWidth, contentHeight) {
681
- const triggerRef = (0, import_react3.useRef)(null);
682
- const { width: windowWidth, height: windowHeight } = (0, import_react_native8.useWindowDimensions)();
683
- const [align, setAlign] = (0, import_react3.useState)("center");
684
- const [verticalAlign, setVerticalAlign] = (0, import_react3.useState)("below");
685
- const [maxHeight, setMaxHeight] = (0, import_react3.useState)(contentHeight);
686
- const [measured, setMeasured] = (0, import_react3.useState)(false);
687
- const [prevOpen, setPrevOpen] = (0, import_react3.useState)(open);
758
+ function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
759
+ if (rotation === 0) return { x, y, width, height };
760
+ const cx = windowWidth / 2;
761
+ const cy = windowHeight / 2;
762
+ const dx = x + width / 2 - cx;
763
+ const dy = y + height / 2 - cy;
764
+ const [newDx, newDy, newWidth, newHeight] = rotation === 180 ? [-dx, -dy, width, height] : rotation === 90 ? [-dy, dx, height, width] : [dy, -dx, height, width];
765
+ return { x: cx + newDx - newWidth / 2, y: cy + newDy - newHeight / 2, width: newWidth, height: newHeight };
766
+ }
767
+ function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
768
+ const triggerRef = (0, import_react5.useRef)(null);
769
+ const { width: windowWidth, height: windowHeight } = (0, import_react_native9.useWindowDimensions)();
770
+ const [align, setAlign] = (0, import_react5.useState)("center");
771
+ const [verticalAlign, setVerticalAlign] = (0, import_react5.useState)("below");
772
+ const [maxHeight, setMaxHeight] = (0, import_react5.useState)(contentHeight);
773
+ const [measured, setMeasured] = (0, import_react5.useState)(false);
774
+ const [prevOpen, setPrevOpen] = (0, import_react5.useState)(open);
688
775
  if (open !== prevOpen) {
689
776
  setPrevOpen(open);
690
777
  if (open) setMeasured(false);
691
778
  }
692
- const measure = (0, import_react3.useCallback)(() => {
693
- triggerRef.current?.measureInWindow((x, y, triggerWidth, triggerHeight) => {
779
+ const measure = (0, import_react5.useCallback)(() => {
780
+ triggerRef.current?.measureInWindow((rawX, rawY, rawWidth, rawHeight) => {
781
+ const { x, y, width: triggerWidth, height: triggerHeight } = rotateRect(rawX, rawY, rawWidth, rawHeight, rotation, windowWidth, windowHeight);
694
782
  const centerX = x + triggerWidth / 2;
695
783
  const overflowsRight = centerX + contentWidth / 2 > windowWidth - EDGE_MARGIN;
696
784
  const overflowsLeft = centerX - contentWidth / 2 < EDGE_MARGIN;
@@ -704,15 +792,275 @@ function useAutoAlign(open, contentWidth, contentHeight) {
704
792
  setMaxHeight(Math.max(above ? roomAbove : roomBelow, 0));
705
793
  setMeasured(true);
706
794
  });
707
- }, [contentWidth, contentHeight, windowWidth, windowHeight]);
708
- (0, import_react3.useEffect)(() => {
795
+ }, [contentWidth, contentHeight, windowWidth, windowHeight, rotation]);
796
+ (0, import_react5.useEffect)(() => {
709
797
  if (open) measure();
710
798
  }, [open, measure]);
711
799
  return { align, maxHeight, measured, triggerRef, verticalAlign };
712
800
  }
713
801
 
802
+ // src/SectionedDropdown.tsx
803
+ var import_jsx_runtime10 = require("react/jsx-runtime");
804
+ var MENU_BORDER_WIDTH = 1;
805
+ var TRIGGER_SIZE = 44;
806
+ var MENU_MIN_WIDTH = 200;
807
+ var MENU_MAX_WIDTH = 220;
808
+ var MENU_ITEM_ICON_SIZE = 18;
809
+ var MENU_ITEM_LABEL_MAX_WIDTH = 150;
810
+ var MENU_ITEM_HEIGHT = 40;
811
+ var MENU_ITEM_HEIGHT_WITH_DESCRIPTION = 58;
812
+ var MENU_DIVIDER_HEIGHT = 9;
813
+ var MENU_VERTICAL_PADDING = 12;
814
+ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accentColor, mutedColor, onAccentColor, dark, align: alignOverride, rotation: rotationOverride, autoDismiss = true, labelFontFamily = MONO_FONT, allClearLabels = { all: "All", clear: "Clear" } }) {
815
+ const ambientRotation = (0, import_core5.useRotation)();
816
+ const rotation = rotationOverride ?? ambientRotation;
817
+ const menuBg = dark ? "#000000" : "#FFFFFF";
818
+ const menuBorderColor = (0, import_auto_paper5.getBlendedColor)(mutedColor, menuBg, 0.5);
819
+ const resolvedOnAccentColor = onAccentColor ?? (0, import_auto_paper5.getColorRoles)(accentColor, menuBg).onColor;
820
+ const fg = dark ? "#FFFFFF" : "#000000";
821
+ const open = host.openId === id;
822
+ const estimatedHeight = MENU_VERTICAL_PADDING + sections.reduce((sum, section, sectionIndex) => {
823
+ const rows = section.options.reduce((rowSum, option) => rowSum + (option.description ? MENU_ITEM_HEIGHT_WITH_DESCRIPTION : MENU_ITEM_HEIGHT), 0);
824
+ const footer = section.kind === "multi" && section.allClear ? MENU_ITEM_HEIGHT : 0;
825
+ const divider = sectionIndex > 0 ? MENU_DIVIDER_HEIGHT : 0;
826
+ return sum + rows + footer + divider;
827
+ }, 0);
828
+ const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, MENU_MAX_WIDTH, estimatedHeight, rotation);
829
+ const align = alignOverride ?? autoAlign;
830
+ const onlySection = sections.length === 1 ? sections[0] : null;
831
+ const selectedOption = onlySection?.kind === "single" ? onlySection.options.find((o) => o.value === onlySection.value) : void 0;
832
+ const triggerIcon = selectedOption?.icon ?? icon;
833
+ const triggerIconSize = selectedOption?.iconSize ?? 22;
834
+ const hasMultiSection = sections.some((s) => s.kind === "multi");
835
+ const anyMultiSelected = sections.some((s) => s.kind === "multi" && s.value.length > 0);
836
+ const triggerIconColor = hasMultiSection ? anyMultiSelected ? accentColor : fg : fg;
837
+ const gaugeSegments = sections.reduce((sum, section) => sum + section.options.length, 0);
838
+ const gaugeLitIndices = [];
839
+ let gaugeOffset = 0;
840
+ for (const section of sections) {
841
+ if (section.kind === "single") {
842
+ const index = section.options.findIndex((o) => o.value === section.value);
843
+ if (index >= 0) gaugeLitIndices.push(gaugeOffset + index);
844
+ } else {
845
+ section.options.forEach((o, i) => {
846
+ if (section.value.includes(o.value)) gaugeLitIndices.push(gaugeOffset + i);
847
+ });
848
+ }
849
+ gaugeOffset += section.options.length;
850
+ }
851
+ const anchorStyle = [styles8.anchor, open && styles8.anchorOpen];
852
+ const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
853
+ const dividerStyle = { backgroundColor: menuBorderColor };
854
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { style: anchorStyle, children: [
855
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { ref: triggerRef, collapsable: false, style: styles8.triggerBox, children: [
856
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
857
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press4.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
858
+ ] }),
859
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native10.ScrollView, { style: [styles8.menu, menuStyle], contentContainerStyle: styles8.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { children: [
860
+ sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native10.View, { style: [styles8.divider, dividerStyle] }),
861
+ section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
862
+ SingleSection,
863
+ {
864
+ section,
865
+ accentColor,
866
+ onAccentColor: resolvedOnAccentColor,
867
+ mutedColor,
868
+ labelFontFamily,
869
+ onSelect: (value) => {
870
+ section.onChange(value);
871
+ if (autoDismiss) host.close();
872
+ }
873
+ }
874
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
875
+ ] }, section.id)) }) })
876
+ ] });
877
+ }
878
+ function SingleSection({
879
+ section,
880
+ accentColor,
881
+ onAccentColor,
882
+ mutedColor,
883
+ labelFontFamily,
884
+ onSelect
885
+ }) {
886
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: section.options.map((option) => {
887
+ const selected = option.value === section.value;
888
+ const taken = !selected && option.value === section.takenValue;
889
+ const rowColor = selected ? onAccentColor : mutedColor;
890
+ const itemStyle = [styles8.item, selected && { backgroundColor: accentColor }, taken && styles8.itemTaken];
891
+ const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
892
+ const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
893
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press4.TouchableRipple, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { style: styles8.itemRow, children: [
894
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
895
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { style: styles8.itemLabelColumn, children: [
896
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
897
+ option.description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
898
+ ] })
899
+ ] }) }, option.value);
900
+ }) });
901
+ }
902
+ function MultiSection({
903
+ section,
904
+ accentColor,
905
+ onAccentColor,
906
+ mutedColor,
907
+ labelFontFamily,
908
+ allClearLabels
909
+ }) {
910
+ const allSelected = section.value.length === section.options.length;
911
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
912
+ section.options.map((option, i) => {
913
+ const selected = section.value.includes(option.value);
914
+ const rowColor = selected ? onAccentColor : mutedColor;
915
+ const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
916
+ const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
917
+ const itemStyle = [styles8.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
918
+ const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
919
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { style: styles8.itemRow, children: [
920
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
921
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Text, { variant: "labelLarge", style: [styles8.itemLabelFlex, labelStyle], children: option.label })
922
+ ] }) }, option.value);
923
+ }),
924
+ section.allClear && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles8.item, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Text, { variant: "labelLarge", style: [styles8.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
925
+ ] });
926
+ }
927
+ var styles8 = import_react_native10.StyleSheet.create({
928
+ allClearLabel: {
929
+ fontWeight: "bold",
930
+ textAlign: "center"
931
+ },
932
+ anchor: {
933
+ position: "relative"
934
+ },
935
+ // React Native Web gives every position:'relative' view an explicit zIndex (0, not 'auto'),
936
+ // which makes each one its own stacking context — so a zIndex set only on the popover content
937
+ // below only wins comparisons within this anchor's own box, not against this anchor's own later
938
+ // siblings (e.g. a Ready button), which are otherwise painted on top by DOM-order tiebreak.
939
+ // Elevating the anchor itself, only while its popover is open, fixes that at the right level.
940
+ anchorOpen: {
941
+ zIndex: 100
942
+ },
943
+ divider: {
944
+ height: import_react_native10.StyleSheet.hairlineWidth,
945
+ marginVertical: 4
946
+ },
947
+ item: {
948
+ borderRadius: 8,
949
+ paddingHorizontal: 12,
950
+ paddingVertical: 8
951
+ },
952
+ itemLabelColumn: {
953
+ maxWidth: MENU_ITEM_LABEL_MAX_WIDTH
954
+ },
955
+ itemLabelFlex: {
956
+ flex: 1
957
+ },
958
+ itemRow: {
959
+ alignItems: "center",
960
+ flexDirection: "row",
961
+ gap: 8
962
+ },
963
+ // Same opacity as ProfilePicker's rowDisabled / InlineColorPicker's swatchTaken — the fleet's
964
+ // established "shown, not removed" treatment for another seat's own current pick.
965
+ itemTaken: {
966
+ opacity: 0.35
967
+ },
968
+ menu: {
969
+ borderRadius: 12,
970
+ borderWidth: MENU_BORDER_WIDTH,
971
+ elevation: 8,
972
+ maxWidth: MENU_MAX_WIDTH,
973
+ minWidth: MENU_MIN_WIDTH,
974
+ shadowColor: "#000000",
975
+ shadowOffset: { height: 2, width: 0 },
976
+ shadowOpacity: 0.3,
977
+ shadowRadius: 8
978
+ },
979
+ // Separate from `menu` itself — a ScrollView's own style sets the scrolling frame's bounds
980
+ // (including the maxHeight that actually caps it), while padding/gap belong to its
981
+ // contentContainerStyle, which sizes to the *unclamped* content instead of the frame.
982
+ menuContent: {
983
+ gap: 4,
984
+ padding: 6
985
+ },
986
+ // Fixed hit-area regardless of the current option's iconSize, so a small-vs-large trigger icon
987
+ // doesn't shift this picker's position within its row.
988
+ triggerBox: {
989
+ alignItems: "center",
990
+ height: TRIGGER_SIZE,
991
+ justifyContent: "center",
992
+ width: TRIGGER_SIZE
993
+ }
994
+ });
995
+
996
+ // src/ControlSchemePicker.tsx
997
+ var import_jsx_runtime11 = require("react/jsx-runtime");
998
+ function ControlSchemePicker({ id, host, value, onChange, options, takenValue, accentColor, mutedColor, onAccentColor, dark, align, labelFontFamily }) {
999
+ const isTouchPrimary = (0, import_core6.useIsTouchPrimaryDevice)();
1000
+ if (import_react_native11.Platform.OS !== "web" || isTouchPrimary) return null;
1001
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SectionedDropdown, { id, host, icon: "keyboard-outline", accessibilityLabel: "Control scheme", sections: [{ kind: "single", id: "controls", options, value, onChange, takenValue }], accentColor, mutedColor, onAccentColor, dark, align, labelFontFamily });
1002
+ }
1003
+
1004
+ // src/CornerActionButtons.tsx
1005
+ var import_feedback_press5 = require("@rific/feedback-press");
1006
+ var import_react_native12 = require("react-native");
1007
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1008
+ function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon = "arrow-left", backLabel = "Back" }) {
1009
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1010
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_feedback_press5.IconButton, { icon: backIcon, iconColor: fg, size: 24, style: [styles9.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: backLabel }),
1011
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_feedback_press5.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles9.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
1012
+ ] });
1013
+ }
1014
+ var styles9 = import_react_native12.StyleSheet.create({
1015
+ back: {
1016
+ position: "absolute"
1017
+ },
1018
+ topRight: {
1019
+ position: "absolute"
1020
+ }
1021
+ });
1022
+
1023
+ // src/CornerStatusBadges.tsx
1024
+ var import_react_native13 = require("react-native");
1025
+ var import_react_native_paper6 = require("react-native-paper");
1026
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1027
+ var BADGE_SIZE = 30;
1028
+ function StatusBadge({ icon }) {
1029
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native13.View, { style: styles10.badge, children: icon && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper6.Icon, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
1030
+ }
1031
+ function CornerStatusBadges({ orientationMode, p1OnRight, p1, p2 }) {
1032
+ const p1OnRightSide = orientationMode === "sideBySide" && p1OnRight;
1033
+ const p2OnLeftSide = orientationMode === "sideBySide" && !p1OnRight;
1034
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1035
+ p1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native13.View, { pointerEvents: "none", style: p1OnRightSide ? styles10.bottomRight : styles10.bottomLeft, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatusBadge, { icon: p1.icon }) }),
1036
+ p2 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native13.View, { pointerEvents: "none", style: p2OnLeftSide ? styles10.topLeft : styles10.topRight, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatusBadge, { icon: p2.icon }) })
1037
+ ] });
1038
+ }
1039
+ var styles10 = import_react_native13.StyleSheet.create({
1040
+ badge: {
1041
+ alignItems: "center",
1042
+ backgroundColor: "rgba(0,0,0,0.28)",
1043
+ borderColor: "rgba(255,255,255,0.18)",
1044
+ borderRadius: BADGE_SIZE / 2,
1045
+ borderWidth: 1,
1046
+ height: BADGE_SIZE,
1047
+ justifyContent: "center",
1048
+ opacity: 0.7,
1049
+ width: BADGE_SIZE
1050
+ },
1051
+ bottomLeft: { bottom: 12, left: 12, position: "absolute" },
1052
+ bottomRight: { bottom: 12, position: "absolute", right: 12 },
1053
+ topLeft: { left: 12, position: "absolute", top: 12 },
1054
+ topRight: { position: "absolute", right: 12, top: 12 }
1055
+ });
1056
+
714
1057
  // src/InlineColorPicker.tsx
715
- var import_jsx_runtime7 = require("react/jsx-runtime");
1058
+ var import_auto_paper6 = require("@rific/auto-paper");
1059
+ var import_feedback_press6 = require("@rific/feedback-press");
1060
+ var import_core7 = require("@tastic/core");
1061
+ var import_react_native14 = require("react-native");
1062
+ var import_react_native_paper7 = require("react-native-paper");
1063
+ var import_jsx_runtime14 = require("react/jsx-runtime");
716
1064
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
717
1065
  var DEFAULT_SIZE = 48;
718
1066
  var SWATCH_SIZE = 28;
@@ -725,30 +1073,32 @@ var SCREEN_MARGIN = 40;
725
1073
  function widthForColumns(columns) {
726
1074
  return SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * columns + SWATCHES_GAP * (columns - 1);
727
1075
  }
728
- function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = import_auto_paper4.defaultColors, takenValue, allowSwapTaken, dark, align: alignOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
1076
+ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = import_auto_paper6.defaultColors, takenValue, allowSwapTaken, dark, align: alignOverride, rotation: rotationOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
1077
+ const ambientRotation = (0, import_core7.useRotation)();
1078
+ const rotation = rotationOverride ?? ambientRotation;
729
1079
  const menuBg = dark ? "#000000" : "#FFFFFF";
730
- const { width: windowWidth } = (0, import_react_native9.useWindowDimensions)();
731
- const autoColumns = (0, import_core3.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
1080
+ const { width: windowWidth } = (0, import_react_native14.useWindowDimensions)();
1081
+ const autoColumns = (0, import_core7.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
732
1082
  const resolvedColumns = columns ?? autoColumns;
733
1083
  const swatchesWidth = widthForColumns(resolvedColumns);
734
1084
  const swatchRows = Math.ceil(swatches.length / resolvedColumns);
735
1085
  const swatchesHeight = SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * swatchRows + SWATCHES_GAP * (swatchRows - 1);
736
1086
  const open = host.openId === id;
737
- const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, swatchesWidth, swatchesHeight);
1087
+ const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, swatchesWidth, swatchesHeight, rotation);
738
1088
  const align = alignOverride ?? autoAlign;
739
1089
  const displayFor = previewValue ?? ((hex) => hex);
740
1090
  const displayValue = displayFor(value);
741
- const contrastColor = (0, import_auto_paper4.getContrastColor)(displayValue);
1091
+ const contrastColor = (0, import_auto_paper6.getContrastColor)(displayValue);
742
1092
  const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
743
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native9.View, { style: [styles7.anchor, open && styles7.anchorOpen], children: [
744
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles7.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native9.View, { ref: triggerRef, collapsable: false, style: styles7.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper4.Text, { style: [styles7.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper4.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
745
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native9.ScrollView, { style: [styles7.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles7.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
1093
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native14.View, { style: [styles11.anchor, open && styles11.anchorOpen], children: [
1094
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press6.TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles11.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native14.View, { ref: triggerRef, collapsable: false, style: styles11.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Text, { style: [styles11.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
1095
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native14.ScrollView, { style: [styles11.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles11.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
746
1096
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
747
1097
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
748
1098
  const swappable = taken && allowSwapTaken;
749
1099
  const swatchDisplay = displayFor(swatch.value);
750
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
751
- import_feedback_press4.TouchableRipple,
1100
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1101
+ import_feedback_press6.TouchableRipple,
752
1102
  {
753
1103
  disabled: taken && !swappable,
754
1104
  onPress: () => {
@@ -756,15 +1106,15 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
756
1106
  if (autoDismiss) host.close();
757
1107
  },
758
1108
  borderless: true,
759
- style: [styles7.swatch, { backgroundColor: swatchDisplay }, selected && styles7.swatchSelected, taken && !swappable && styles7.swatchTaken],
760
- children: selected ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper4.Icon, { source: "check", size: 16, color: (0, import_auto_paper4.getContrastColor)(swatchDisplay) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper4.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper4.getContrastColor)(swatchDisplay) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper4.Icon, { source: "close", size: 16, color: (0, import_auto_paper4.getContrastColor)(swatchDisplay) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native9.View, {})
1109
+ style: [styles11.swatch, { backgroundColor: swatchDisplay }, selected && styles11.swatchSelected, taken && !swappable && styles11.swatchTaken],
1110
+ children: selected ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: "check", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: "close", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native14.View, {})
761
1111
  },
762
1112
  swatch.value
763
1113
  );
764
1114
  }) }) })
765
1115
  ] });
766
1116
  }
767
- var styles7 = import_react_native9.StyleSheet.create({
1117
+ var styles11 = import_react_native14.StyleSheet.create({
768
1118
  anchor: {
769
1119
  position: "relative"
770
1120
  },
@@ -836,11 +1186,11 @@ var styles7 = import_react_native9.StyleSheet.create({
836
1186
  });
837
1187
 
838
1188
  // src/LabeledDropdown.tsx
839
- var import_auto_paper5 = require("@rific/auto-paper");
840
- var import_feedback_press5 = require("@rific/feedback-press");
841
- var import_react_native10 = require("react-native");
842
- var import_react_native_paper5 = require("react-native-paper");
843
- var import_jsx_runtime8 = require("react/jsx-runtime");
1189
+ var import_auto_paper7 = require("@rific/auto-paper");
1190
+ var import_feedback_press7 = require("@rific/feedback-press");
1191
+ var import_react_native15 = require("react-native");
1192
+ var import_react_native_paper8 = require("react-native-paper");
1193
+ var import_jsx_runtime15 = require("react/jsx-runtime");
844
1194
  var TRIGGER_HEIGHT = 28;
845
1195
  var POPOVER_WIDTH = 180;
846
1196
  var ROW_HEIGHT = 36;
@@ -863,22 +1213,22 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
863
1213
  onChange(option.value);
864
1214
  host.close();
865
1215
  };
866
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native10.View, { style: [styles8.anchor, open && styles8.anchorOpen], children: [
867
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press5.TouchableRipple, { onPress: () => host.toggle(id), style: styles8.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native10.View, { ref: triggerRef, collapsable: false, style: styles8.triggerInner, children: [
868
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper5.Text, { style: [styles8.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
869
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper5.Icon, { source: "menu-down", size: 16, color })
1216
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native15.View, { style: [styles12.anchor, open && styles12.anchorOpen], children: [
1217
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => host.toggle(id), style: styles12.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native15.View, { ref: triggerRef, collapsable: false, style: styles12.triggerInner, children: [
1218
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper8.Text, { style: [styles12.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
1219
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper8.Icon, { source: "menu-down", size: 16, color })
870
1220
  ] }) }),
871
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native10.ScrollView, { style: [styles8.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles8.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
1221
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native15.ScrollView, { style: [styles12.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles12.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
872
1222
  const isSelected = option.value === value;
873
- const onColor = (0, import_auto_paper5.getContrastColor)(color);
874
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press5.TouchableRipple, { onPress: () => handleSelect(option), style: [styles8.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native10.View, { style: styles8.rowInner, children: [
875
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper5.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
876
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper5.Text, { style: [styles8.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
1223
+ const onColor = (0, import_auto_paper7.getContrastColor)(color);
1224
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => handleSelect(option), style: [styles12.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native15.View, { style: styles12.rowInner, children: [
1225
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper8.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
1226
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper8.Text, { style: [styles12.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
877
1227
  ] }) }, option.value);
878
1228
  }) }) })
879
1229
  ] });
880
1230
  }
881
- var styles8 = import_react_native10.StyleSheet.create({
1231
+ var styles12 = import_react_native15.StyleSheet.create({
882
1232
  anchor: {
883
1233
  position: "relative"
884
1234
  },
@@ -936,25 +1286,25 @@ function loadSkiaWeb() {
936
1286
  }
937
1287
 
938
1288
  // src/PressAwayOverlay.tsx
939
- var import_react_native11 = require("react-native");
940
- var import_jsx_runtime9 = require("react/jsx-runtime");
1289
+ var import_react_native16 = require("react-native");
1290
+ var import_jsx_runtime16 = require("react/jsx-runtime");
941
1291
  function PressAwayOverlay({ active, onPress, style }) {
942
1292
  if (!active) return null;
943
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native11.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native11.StyleSheet.absoluteFill, style] });
1293
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native16.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native16.StyleSheet.absoluteFill, style] });
944
1294
  }
945
1295
 
946
1296
  // src/ReadyButton.tsx
947
- var import_feedback_press6 = require("@rific/feedback-press");
948
- var import_react_native12 = require("react-native");
949
- var import_react_native_paper6 = require("react-native-paper");
950
- var import_jsx_runtime10 = require("react/jsx-runtime");
1297
+ var import_feedback_press8 = require("@rific/feedback-press");
1298
+ var import_react_native17 = require("react-native");
1299
+ var import_react_native_paper9 = require("react-native-paper");
1300
+ var import_jsx_runtime17 = require("react/jsx-runtime");
951
1301
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
952
- const readyButtonStyle = [styles9.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
1302
+ const readyButtonStyle = [styles13.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
953
1303
  const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
954
1304
  const labelColorStyle = { color: ready ? "#000000" : color };
955
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press6.TouchableRipple, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper6.Text, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
1305
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_feedback_press8.TouchableRipple, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native_paper9.Text, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
956
1306
  }
957
- var styles9 = import_react_native12.StyleSheet.create({
1307
+ var styles13 = import_react_native17.StyleSheet.create({
958
1308
  readyButton: {
959
1309
  alignItems: "center",
960
1310
  borderRadius: 12,
@@ -966,228 +1316,22 @@ var styles9 = import_react_native12.StyleSheet.create({
966
1316
  }
967
1317
  });
968
1318
 
969
- // src/SectionedDropdown.tsx
970
- var import_auto_paper6 = require("@rific/auto-paper");
971
- var import_feedback_press7 = require("@rific/feedback-press");
972
- var import_react_native14 = require("react-native");
973
- var import_react_native_paper7 = require("react-native-paper");
974
-
975
- // src/TriggerGaugeHost.tsx
976
- var import_react5 = require("react");
977
- var import_jsx_runtime12 = require("react/jsx-runtime");
978
- var LazyTriggerGauge = (0, import_react5.lazy)(() => loadSkiaWeb().then(() => Promise.resolve().then(() => (init_TriggerGauge(), TriggerGauge_exports)).then((m) => ({ default: m.TriggerGauge }))));
979
- function TriggerGaugeHost(props) {
980
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react5.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(LazyTriggerGauge, { ...props }) });
981
- }
982
-
983
- // src/SectionedDropdown.tsx
984
- var import_jsx_runtime13 = require("react/jsx-runtime");
985
- var MENU_BORDER_WIDTH = 1;
986
- var TRIGGER_SIZE = 44;
987
- var MENU_MIN_WIDTH = 200;
988
- var MENU_MAX_WIDTH = 220;
989
- var MENU_ITEM_ICON_SIZE = 18;
990
- var MENU_ITEM_LABEL_MAX_WIDTH = 150;
991
- var MENU_ITEM_HEIGHT = 40;
992
- var MENU_ITEM_HEIGHT_WITH_DESCRIPTION = 58;
993
- var MENU_DIVIDER_HEIGHT = 9;
994
- var MENU_VERTICAL_PADDING = 12;
995
- function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accentColor, mutedColor, onAccentColor, dark, align: alignOverride, autoDismiss = true, labelFontFamily = MONO_FONT, allClearLabels = { all: "All", clear: "Clear" } }) {
996
- const menuBg = dark ? "#000000" : "#FFFFFF";
997
- const menuBorderColor = (0, import_auto_paper6.getBlendedColor)(mutedColor, menuBg, 0.5);
998
- const resolvedOnAccentColor = onAccentColor ?? (0, import_auto_paper6.getColorRoles)(accentColor, menuBg).onColor;
999
- const fg = dark ? "#FFFFFF" : "#000000";
1000
- const open = host.openId === id;
1001
- const estimatedHeight = MENU_VERTICAL_PADDING + sections.reduce((sum, section, sectionIndex) => {
1002
- const rows = section.options.reduce((rowSum, option) => rowSum + (option.description ? MENU_ITEM_HEIGHT_WITH_DESCRIPTION : MENU_ITEM_HEIGHT), 0);
1003
- const footer = section.kind === "multi" && section.allClear ? MENU_ITEM_HEIGHT : 0;
1004
- const divider = sectionIndex > 0 ? MENU_DIVIDER_HEIGHT : 0;
1005
- return sum + rows + footer + divider;
1006
- }, 0);
1007
- const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, MENU_MAX_WIDTH, estimatedHeight);
1008
- const align = alignOverride ?? autoAlign;
1009
- const onlySection = sections.length === 1 ? sections[0] : null;
1010
- const selectedOption = onlySection?.kind === "single" ? onlySection.options.find((o) => o.value === onlySection.value) : void 0;
1011
- const triggerIcon = selectedOption?.icon ?? icon;
1012
- const triggerIconSize = selectedOption?.iconSize ?? 22;
1013
- const hasMultiSection = sections.some((s) => s.kind === "multi");
1014
- const anyMultiSelected = sections.some((s) => s.kind === "multi" && s.value.length > 0);
1015
- const triggerIconColor = hasMultiSection ? anyMultiSelected ? accentColor : fg : fg;
1016
- const gaugeSegments = sections.reduce((sum, section) => sum + section.options.length, 0);
1017
- const gaugeLitIndices = [];
1018
- let gaugeOffset = 0;
1019
- for (const section of sections) {
1020
- if (section.kind === "single") {
1021
- const index = section.options.findIndex((o) => o.value === section.value);
1022
- if (index >= 0) gaugeLitIndices.push(gaugeOffset + index);
1023
- } else {
1024
- section.options.forEach((o, i) => {
1025
- if (section.value.includes(o.value)) gaugeLitIndices.push(gaugeOffset + i);
1026
- });
1027
- }
1028
- gaugeOffset += section.options.length;
1029
- }
1030
- const anchorStyle = [styles11.anchor, open && styles11.anchorOpen];
1031
- const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
1032
- const dividerStyle = { backgroundColor: menuBorderColor };
1033
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native14.View, { style: anchorStyle, children: [
1034
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native14.View, { ref: triggerRef, collapsable: false, style: styles11.triggerBox, children: [
1035
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
1036
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press7.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
1037
- ] }),
1038
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native14.ScrollView, { style: [styles11.menu, menuStyle], contentContainerStyle: styles11.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native14.View, { children: [
1039
- sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native14.View, { style: [styles11.divider, dividerStyle] }),
1040
- section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1041
- SingleSection,
1042
- {
1043
- section,
1044
- accentColor,
1045
- onAccentColor: resolvedOnAccentColor,
1046
- mutedColor,
1047
- labelFontFamily,
1048
- onSelect: (value) => {
1049
- section.onChange(value);
1050
- if (autoDismiss) host.close();
1051
- }
1052
- }
1053
- ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
1054
- ] }, section.id)) }) })
1055
- ] });
1056
- }
1057
- function SingleSection({
1058
- section,
1059
- accentColor,
1060
- onAccentColor,
1061
- mutedColor,
1062
- labelFontFamily,
1063
- onSelect
1064
- }) {
1065
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_jsx_runtime13.Fragment, { children: section.options.map((option) => {
1066
- const selected = option.value === section.value;
1067
- const taken = !selected && option.value === section.takenValue;
1068
- const rowColor = selected ? onAccentColor : mutedColor;
1069
- const itemStyle = [styles11.item, selected && { backgroundColor: accentColor }, taken && styles11.itemTaken];
1070
- const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
1071
- const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
1072
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press7.TouchableRipple, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native14.View, { style: styles11.itemRow, children: [
1073
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper7.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1074
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native14.View, { style: styles11.itemLabelColumn, children: [
1075
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper7.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
1076
- option.description && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper7.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
1077
- ] })
1078
- ] }) }, option.value);
1079
- }) });
1080
- }
1081
- function MultiSection({
1082
- section,
1083
- accentColor,
1084
- onAccentColor,
1085
- mutedColor,
1086
- labelFontFamily,
1087
- allClearLabels
1088
- }) {
1089
- const allSelected = section.value.length === section.options.length;
1090
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1091
- section.options.map((option, i) => {
1092
- const selected = section.value.includes(option.value);
1093
- const rowColor = selected ? onAccentColor : mutedColor;
1094
- const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
1095
- const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
1096
- const itemStyle = [styles11.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
1097
- const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
1098
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native14.View, { style: styles11.itemRow, children: [
1099
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper7.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1100
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper7.Text, { variant: "labelLarge", style: [styles11.itemLabelFlex, labelStyle], children: option.label })
1101
- ] }) }, option.value);
1102
- }),
1103
- section.allClear && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles11.item, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper7.Text, { variant: "labelLarge", style: [styles11.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
1104
- ] });
1105
- }
1106
- var styles11 = import_react_native14.StyleSheet.create({
1107
- allClearLabel: {
1108
- fontWeight: "bold",
1109
- textAlign: "center"
1110
- },
1111
- anchor: {
1112
- position: "relative"
1113
- },
1114
- // React Native Web gives every position:'relative' view an explicit zIndex (0, not 'auto'),
1115
- // which makes each one its own stacking context — so a zIndex set only on the popover content
1116
- // below only wins comparisons within this anchor's own box, not against this anchor's own later
1117
- // siblings (e.g. a Ready button), which are otherwise painted on top by DOM-order tiebreak.
1118
- // Elevating the anchor itself, only while its popover is open, fixes that at the right level.
1119
- anchorOpen: {
1120
- zIndex: 100
1121
- },
1122
- divider: {
1123
- height: import_react_native14.StyleSheet.hairlineWidth,
1124
- marginVertical: 4
1125
- },
1126
- item: {
1127
- borderRadius: 8,
1128
- paddingHorizontal: 12,
1129
- paddingVertical: 8
1130
- },
1131
- itemLabelColumn: {
1132
- maxWidth: MENU_ITEM_LABEL_MAX_WIDTH
1133
- },
1134
- itemLabelFlex: {
1135
- flex: 1
1136
- },
1137
- itemRow: {
1138
- alignItems: "center",
1139
- flexDirection: "row",
1140
- gap: 8
1141
- },
1142
- // Same opacity as ProfilePicker's rowDisabled / InlineColorPicker's swatchTaken — the fleet's
1143
- // established "shown, not removed" treatment for another seat's own current pick.
1144
- itemTaken: {
1145
- opacity: 0.35
1146
- },
1147
- menu: {
1148
- borderRadius: 12,
1149
- borderWidth: MENU_BORDER_WIDTH,
1150
- elevation: 8,
1151
- maxWidth: MENU_MAX_WIDTH,
1152
- minWidth: MENU_MIN_WIDTH,
1153
- shadowColor: "#000000",
1154
- shadowOffset: { height: 2, width: 0 },
1155
- shadowOpacity: 0.3,
1156
- shadowRadius: 8
1157
- },
1158
- // Separate from `menu` itself — a ScrollView's own style sets the scrolling frame's bounds
1159
- // (including the maxHeight that actually caps it), while padding/gap belong to its
1160
- // contentContainerStyle, which sizes to the *unclamped* content instead of the frame.
1161
- menuContent: {
1162
- gap: 4,
1163
- padding: 6
1164
- },
1165
- // Fixed hit-area regardless of the current option's iconSize, so a small-vs-large trigger icon
1166
- // doesn't shift this picker's position within its row.
1167
- triggerBox: {
1168
- alignItems: "center",
1169
- height: TRIGGER_SIZE,
1170
- justifyContent: "center",
1171
- width: TRIGGER_SIZE
1172
- }
1173
- });
1174
-
1175
1319
  // src/SharedActionBand.tsx
1176
- var import_feedback_press8 = require("@rific/feedback-press");
1177
- var import_react_native15 = require("react-native");
1178
- var import_jsx_runtime14 = require("react/jsx-runtime");
1320
+ var import_feedback_press9 = require("@rific/feedback-press");
1321
+ var import_react_native18 = require("react-native");
1322
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1179
1323
  function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
1180
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native15.View, { style: [styles12.column, popoverOpen && styles12.columnOpen], children: [
1181
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native15.View, { style: styles12.actionsRow, children: [
1182
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press8.IconButton, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1183
- onRandomize && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press8.IconButton, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1184
- onReset && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press8.IconButton, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
1185
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press8.IconButton, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
1324
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native18.View, { style: [styles14.column, popoverOpen && styles14.columnOpen], children: [
1325
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native18.View, { style: styles14.actionsRow, children: [
1326
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press9.IconButton, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1327
+ onRandomize && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press9.IconButton, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1328
+ onReset && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press9.IconButton, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
1329
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press9.IconButton, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
1186
1330
  ] }),
1187
1331
  children
1188
1332
  ] });
1189
1333
  }
1190
- var styles12 = import_react_native15.StyleSheet.create({
1334
+ var styles14 = import_react_native18.StyleSheet.create({
1191
1335
  actionsRow: {
1192
1336
  alignItems: "center",
1193
1337
  flexDirection: "row",
@@ -1203,20 +1347,20 @@ var styles12 = import_react_native15.StyleSheet.create({
1203
1347
  });
1204
1348
 
1205
1349
  // src/StatRow.tsx
1206
- var import_auto_paper7 = require("@rific/auto-paper");
1207
- var import_react_native16 = require("react-native");
1208
- var import_react_native_paper8 = require("react-native-paper");
1209
- var import_jsx_runtime15 = require("react/jsx-runtime");
1350
+ var import_auto_paper8 = require("@rific/auto-paper");
1351
+ var import_react_native19 = require("react-native");
1352
+ var import_react_native_paper10 = require("react-native-paper");
1353
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1210
1354
  function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }) {
1211
- const { dark } = (0, import_auto_paper7.useAutoPaperTheme)();
1355
+ const { dark } = (0, import_auto_paper8.useAutoPaperTheme)();
1212
1356
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
1213
1357
  const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
1214
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { style: styles13.row, children: [
1215
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper8.Text, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
1216
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper8.Text, { variant: "bodyMedium", style: [styles13.value, { color: fg }], children: value })
1358
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native19.View, { style: styles15.row, children: [
1359
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper10.Text, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
1360
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper10.Text, { variant: "bodyMedium", style: [styles15.value, { color: fg }], children: value })
1217
1361
  ] });
1218
1362
  }
1219
- var styles13 = import_react_native16.StyleSheet.create({
1363
+ var styles15 = import_react_native19.StyleSheet.create({
1220
1364
  row: {
1221
1365
  alignItems: "center",
1222
1366
  flexDirection: "row",
@@ -1228,20 +1372,20 @@ var styles13 = import_react_native16.StyleSheet.create({
1228
1372
  });
1229
1373
 
1230
1374
  // src/StatSection.tsx
1231
- var import_auto_paper8 = require("@rific/auto-paper");
1232
- var import_react_native17 = require("react-native");
1233
- var import_react_native_paper9 = require("react-native-paper");
1234
- var import_jsx_runtime16 = require("react/jsx-runtime");
1375
+ var import_auto_paper9 = require("@rific/auto-paper");
1376
+ var import_react_native20 = require("react-native");
1377
+ var import_react_native_paper11 = require("react-native-paper");
1378
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1235
1379
  function StatSection({ label, fg: fgOverride, sectionBg: sectionBgOverride, children }) {
1236
- const { dark } = (0, import_auto_paper8.useAutoPaperTheme)();
1380
+ const { dark } = (0, import_auto_paper9.useAutoPaperTheme)();
1237
1381
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
1238
1382
  const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
1239
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native17.View, { style: [styles14.section, { backgroundColor: sectionBg }], children: [
1240
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper9.Text, { variant: "labelMedium", style: [styles14.label, { color: fg }], children: label }),
1383
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native20.View, { style: [styles16.section, { backgroundColor: sectionBg }], children: [
1384
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper11.Text, { variant: "labelMedium", style: [styles16.label, { color: fg }], children: label }),
1241
1385
  children
1242
1386
  ] });
1243
1387
  }
1244
- var styles14 = import_react_native17.StyleSheet.create({
1388
+ var styles16 = import_react_native20.StyleSheet.create({
1245
1389
  // Bold and full-contrast (fg), not the original fontless/fgMuted treatment — that read at the
1246
1390
  // same visual weight as StatRow's own row labels below it (both used the identical muted color;
1247
1391
  // letterSpacing/variant alone didn't separate them enough to read as "this is the group header"
@@ -1272,8 +1416,11 @@ function usePopoverHost() {
1272
1416
  AchievementRow,
1273
1417
  BaseSettingsDialog,
1274
1418
  BaseStatsScreen,
1419
+ ConfirmDialog,
1275
1420
  ContentGutter,
1421
+ ControlSchemePicker,
1276
1422
  CornerActionButtons,
1423
+ CornerStatusBadges,
1277
1424
  InlineColorPicker,
1278
1425
  LABELED_DROPDOWN_POPOVER_WIDTH,
1279
1426
  LOCKED_BADGE_COLOR,
@@ -1284,6 +1431,7 @@ function usePopoverHost() {
1284
1431
  ReadyButton,
1285
1432
  SectionedDropdown,
1286
1433
  SharedActionBand,
1434
+ SkiaGate,
1287
1435
  StatRow,
1288
1436
  StatSection,
1289
1437
  TriggerGaugeHost,