@tastic/hud 0.7.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
@@ -28,22 +28,22 @@ __export(TriggerGauge_exports, {
28
28
  function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, dashWidth, startDeg = DEFAULT_START_DEG, endDeg = startDeg }) {
29
29
  const totalDurationMs = MOUNT_DURATION_MS + Math.max(0, segments - 1) * MOUNT_STAGGER_MS;
30
30
  const mountProgress = (0, import_react_native_reanimated.useSharedValue)(0);
31
- (0, import_react3.useLayoutEffect)(() => {
31
+ (0, import_react4.useLayoutEffect)(() => {
32
32
  mountProgress.value = (0, import_react_native_reanimated.withTiming)(1, { duration: totalDurationMs, easing: import_react_native_reanimated.Easing.out(import_react_native_reanimated.Easing.cubic) });
33
33
  }, []);
34
34
  const rawSweepDeg = ((endDeg - startDeg) % 360 + 360) % 360;
35
35
  const totalSweepDeg = rawSweepDeg === 0 ? 360 : rawSweepDeg;
36
36
  const step = segments > 0 ? totalSweepDeg / segments : 0;
37
- const centerDeg = (0, import_react3.useCallback)((i) => startDeg + (i + 0.5) * step, [startDeg, step]);
37
+ const centerDeg = (0, import_react4.useCallback)((i) => startDeg + (i + 0.5) * step, [startDeg, step]);
38
38
  const singleSelect = litIndices.length === 1;
39
39
  const angleDeg = (0, import_react_native_reanimated.useSharedValue)(centerDeg(litIndices[0] ?? 0));
40
40
  const litIndicesKey = [...litIndices].sort((a, b) => a - b).join(",");
41
- const [transitionFrom, setTransitionFrom] = (0, import_react3.useState)(litIndices);
41
+ const [transitionFrom, setTransitionFrom] = (0, import_react4.useState)(litIndices);
42
42
  const transitionFromKey = [...transitionFrom].sort((a, b) => a - b).join(",");
43
- const lastKeyRef = (0, import_react3.useRef)(litIndicesKey);
44
- const lastIndicesRef = (0, import_react3.useRef)(litIndices);
43
+ const lastKeyRef = (0, import_react4.useRef)(litIndicesKey);
44
+ const lastIndicesRef = (0, import_react4.useRef)(litIndices);
45
45
  const selectionProgress = (0, import_react_native_reanimated.useSharedValue)(1);
46
- (0, import_react3.useLayoutEffect)(() => {
46
+ (0, import_react4.useLayoutEffect)(() => {
47
47
  if (litIndicesKey !== lastKeyRef.current) {
48
48
  if (singleSelect && lastIndicesRef.current.length === 1) {
49
49
  angleDeg.value = (0, import_react_native_reanimated.withTiming)(centerDeg(litIndices[0]), { duration: SELECTION_TRANSITION_DURATION_MS, easing: import_react_native_reanimated.Easing.out(import_react_native_reanimated.Easing.cubic) });
@@ -72,7 +72,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
72
72
  if (mountProgress.value < 1) {
73
73
  const startFrac = i * MOUNT_STAGGER_MS / totalDurationMs;
74
74
  const endFrac = startFrac + MOUNT_DURATION_MS / totalDurationMs;
75
- local = (0, import_core3.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
75
+ local = (0, import_core4.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
76
76
  }
77
77
  const sweep = dashAngleDeg * local;
78
78
  if (sweep <= 0) continue;
@@ -96,7 +96,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
96
96
  if (!isLitNow) continue;
97
97
  const startFrac = i * MOUNT_STAGGER_MS / totalDurationMs;
98
98
  const endFrac = startFrac + MOUNT_DURATION_MS / totalDurationMs;
99
- local = (0, import_core3.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
99
+ local = (0, import_core4.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
100
100
  } else if (isLitNow === wasLit) {
101
101
  local = 1;
102
102
  } else if (isLitNow) {
@@ -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_runtime7.jsx)(import_react_native8.View, { style: [styles7.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
124
- /* @__PURE__ */ (0, import_jsx_runtime7.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_runtime7.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_core3, import_react3, import_react_native8, import_react_native_reanimated, import_jsx_runtime7, 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;
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
- import_core3 = require("@tastic/core");
134
- import_react3 = require("react");
133
+ import_core4 = require("@tastic/core");
134
+ import_react4 = require("react");
135
135
  import_react_native8 = require("react-native");
136
136
  import_react_native_reanimated = require("react-native-reanimated");
137
- import_jsx_runtime7 = 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;
@@ -175,6 +175,7 @@ __export(index_exports, {
175
175
  ReadyButton: () => ReadyButton,
176
176
  SectionedDropdown: () => SectionedDropdown,
177
177
  SharedActionBand: () => SharedActionBand,
178
+ SkiaGate: () => SkiaGate,
178
179
  StatRow: () => StatRow,
179
180
  StatSection: () => StatSection,
180
181
  TriggerGaugeHost: () => TriggerGaugeHost,
@@ -265,8 +266,10 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
265
266
  function SettingIcon({ source, color, containerColor }) {
266
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 }) });
267
268
  }
268
- 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 }) {
269
270
  const { dark, colors } = (0, import_auto_paper2.useAutoPaperTheme)();
271
+ const ambientRotation = (0, import_core.useRotation)();
272
+ const rotation = rotationOverride ?? ambientRotation;
270
273
  const isTouchPrimary = (0, import_core.useIsTouchPrimaryDevice)();
271
274
  const { settings: hapticSettings, set: setHapticSettings } = (0, import_feedback_press.useHapticSettings)();
272
275
  const { settings: soundSettings, set: setSoundSettings } = (0, import_feedback_press.useSoundSettings)();
@@ -458,12 +461,15 @@ var styles2 = import_react_native2.StyleSheet.create({
458
461
  // src/BaseStatsScreen.tsx
459
462
  var import_auto_paper3 = require("@rific/auto-paper");
460
463
  var import_feedback_press2 = require("@rific/feedback-press");
464
+ var import_core2 = require("@tastic/core");
461
465
  var import_react2 = require("react");
462
466
  var import_react_native3 = require("react-native");
463
467
  var import_react_native_paper3 = require("react-native-paper");
464
468
  var import_jsx_runtime3 = require("react/jsx-runtime");
465
- 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 }) {
466
470
  const { dark, colors } = (0, import_auto_paper3.useAutoPaperTheme)();
471
+ const ambientRotation = (0, import_core2.useRotation)();
472
+ const rotation = rotationOverride ?? ambientRotation;
467
473
  const [confirmResetVisible, setConfirmResetVisible] = (0, import_react2.useState)(false);
468
474
  const showReset = !!onReset;
469
475
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
@@ -616,12 +622,12 @@ var styles4 = import_react_native4.StyleSheet.create({
616
622
  });
617
623
 
618
624
  // src/ContentGutter.tsx
619
- var import_core2 = require("@tastic/core");
625
+ var import_core3 = require("@tastic/core");
620
626
  var import_react_native5 = require("react-native");
621
627
  var import_jsx_runtime5 = require("react/jsx-runtime");
622
628
  function ContentGutter({ maxContentWidth, leftGutter, rightGutter, style, children }) {
623
629
  const { width: windowWidth } = (0, import_react_native5.useWindowDimensions)();
624
- const { contentWidth, gutterWidth } = (0, import_core2.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
630
+ const { contentWidth, gutterWidth } = (0, import_core3.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
625
631
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: [styles5.row, style], children: [
626
632
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: [styles5.gutter, { width: gutterWidth }], children: leftGutter }),
627
633
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: { width: contentWidth }, children }),
@@ -640,12 +646,13 @@ var styles5 = import_react_native5.StyleSheet.create({
640
646
  });
641
647
 
642
648
  // src/ControlSchemePicker.tsx
643
- var import_core4 = require("@tastic/core");
649
+ var import_core6 = require("@tastic/core");
644
650
  var import_react_native11 = require("react-native");
645
651
 
646
652
  // src/SectionedDropdown.tsx
647
653
  var import_auto_paper5 = require("@rific/auto-paper");
648
654
  var import_feedback_press4 = require("@rific/feedback-press");
655
+ var import_core5 = require("@tastic/core");
649
656
  var import_react_native10 = require("react-native");
650
657
  var import_react_native_paper5 = require("react-native-paper");
651
658
 
@@ -719,26 +726,45 @@ var styles6 = import_react_native7.StyleSheet.create({
719
726
  }
720
727
  });
721
728
 
722
- // src/TriggerGaugeHost.tsx
723
- var import_react4 = require("react");
724
-
725
- // src/loadSkiaWeb.ts
726
- function loadSkiaWeb() {
727
- return Promise.resolve();
729
+ // src/SkiaGate.tsx
730
+ var import_react3 = require("react");
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 });
728
745
  }
729
746
 
730
747
  // src/TriggerGaugeHost.tsx
731
- var import_jsx_runtime8 = require("react/jsx-runtime");
732
- var LazyTriggerGauge = (0, import_react4.lazy)(() => loadSkiaWeb().then(() => Promise.resolve().then(() => (init_TriggerGauge(), TriggerGauge_exports)).then((m) => ({ default: m.TriggerGauge }))));
748
+ var import_jsx_runtime9 = require("react/jsx-runtime");
749
+ var getTriggerGauge = () => Promise.resolve().then(() => (init_TriggerGauge(), TriggerGauge_exports)).then((mod) => ({ default: mod.TriggerGauge }));
733
750
  function TriggerGaugeHost(props) {
734
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react4.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LazyTriggerGauge, { ...props }) });
751
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
735
752
  }
736
753
 
737
754
  // src/useAutoAlign.ts
738
755
  var import_react5 = require("react");
739
756
  var import_react_native9 = require("react-native");
740
757
  var EDGE_MARGIN = 12;
741
- function useAutoAlign(open, contentWidth, contentHeight) {
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) {
742
768
  const triggerRef = (0, import_react5.useRef)(null);
743
769
  const { width: windowWidth, height: windowHeight } = (0, import_react_native9.useWindowDimensions)();
744
770
  const [align, setAlign] = (0, import_react5.useState)("center");
@@ -751,7 +777,8 @@ function useAutoAlign(open, contentWidth, contentHeight) {
751
777
  if (open) setMeasured(false);
752
778
  }
753
779
  const measure = (0, import_react5.useCallback)(() => {
754
- triggerRef.current?.measureInWindow((x, y, triggerWidth, triggerHeight) => {
780
+ triggerRef.current?.measureInWindow((rawX, rawY, rawWidth, rawHeight) => {
781
+ const { x, y, width: triggerWidth, height: triggerHeight } = rotateRect(rawX, rawY, rawWidth, rawHeight, rotation, windowWidth, windowHeight);
755
782
  const centerX = x + triggerWidth / 2;
756
783
  const overflowsRight = centerX + contentWidth / 2 > windowWidth - EDGE_MARGIN;
757
784
  const overflowsLeft = centerX - contentWidth / 2 < EDGE_MARGIN;
@@ -765,7 +792,7 @@ function useAutoAlign(open, contentWidth, contentHeight) {
765
792
  setMaxHeight(Math.max(above ? roomAbove : roomBelow, 0));
766
793
  setMeasured(true);
767
794
  });
768
- }, [contentWidth, contentHeight, windowWidth, windowHeight]);
795
+ }, [contentWidth, contentHeight, windowWidth, windowHeight, rotation]);
769
796
  (0, import_react5.useEffect)(() => {
770
797
  if (open) measure();
771
798
  }, [open, measure]);
@@ -773,7 +800,7 @@ function useAutoAlign(open, contentWidth, contentHeight) {
773
800
  }
774
801
 
775
802
  // src/SectionedDropdown.tsx
776
- var import_jsx_runtime9 = require("react/jsx-runtime");
803
+ var import_jsx_runtime10 = require("react/jsx-runtime");
777
804
  var MENU_BORDER_WIDTH = 1;
778
805
  var TRIGGER_SIZE = 44;
779
806
  var MENU_MIN_WIDTH = 200;
@@ -784,7 +811,9 @@ var MENU_ITEM_HEIGHT = 40;
784
811
  var MENU_ITEM_HEIGHT_WITH_DESCRIPTION = 58;
785
812
  var MENU_DIVIDER_HEIGHT = 9;
786
813
  var MENU_VERTICAL_PADDING = 12;
787
- function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accentColor, mutedColor, onAccentColor, dark, align: alignOverride, autoDismiss = true, labelFontFamily = MONO_FONT, allClearLabels = { all: "All", clear: "Clear" } }) {
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;
788
817
  const menuBg = dark ? "#000000" : "#FFFFFF";
789
818
  const menuBorderColor = (0, import_auto_paper5.getBlendedColor)(mutedColor, menuBg, 0.5);
790
819
  const resolvedOnAccentColor = onAccentColor ?? (0, import_auto_paper5.getColorRoles)(accentColor, menuBg).onColor;
@@ -796,7 +825,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
796
825
  const divider = sectionIndex > 0 ? MENU_DIVIDER_HEIGHT : 0;
797
826
  return sum + rows + footer + divider;
798
827
  }, 0);
799
- const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, MENU_MAX_WIDTH, estimatedHeight);
828
+ const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, MENU_MAX_WIDTH, estimatedHeight, rotation);
800
829
  const align = alignOverride ?? autoAlign;
801
830
  const onlySection = sections.length === 1 ? sections[0] : null;
802
831
  const selectedOption = onlySection?.kind === "single" ? onlySection.options.find((o) => o.value === onlySection.value) : void 0;
@@ -822,14 +851,14 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
822
851
  const anchorStyle = [styles8.anchor, open && styles8.anchorOpen];
823
852
  const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
824
853
  const dividerStyle = { backgroundColor: menuBorderColor };
825
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: anchorStyle, children: [
826
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { ref: triggerRef, collapsable: false, style: styles8.triggerBox, children: [
827
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
828
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press4.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
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) })
829
858
  ] }),
830
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.ScrollView, { style: [styles8.menu, menuStyle], contentContainerStyle: styles8.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { children: [
831
- sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: [styles8.divider, dividerStyle] }),
832
- section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
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)(
833
862
  SingleSection,
834
863
  {
835
864
  section,
@@ -842,7 +871,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
842
871
  if (autoDismiss) host.close();
843
872
  }
844
873
  }
845
- ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
874
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
846
875
  ] }, section.id)) }) })
847
876
  ] });
848
877
  }
@@ -854,18 +883,18 @@ function SingleSection({
854
883
  labelFontFamily,
855
884
  onSelect
856
885
  }) {
857
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: section.options.map((option) => {
886
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: section.options.map((option) => {
858
887
  const selected = option.value === section.value;
859
888
  const taken = !selected && option.value === section.takenValue;
860
889
  const rowColor = selected ? onAccentColor : mutedColor;
861
890
  const itemStyle = [styles8.item, selected && { backgroundColor: accentColor }, taken && styles8.itemTaken];
862
891
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
863
892
  const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
864
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press4.TouchableRipple, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.itemRow, children: [
865
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper5.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
866
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.itemLabelColumn, children: [
867
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper5.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
868
- option.description && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper5.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
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 })
869
898
  ] })
870
899
  ] }) }, option.value);
871
900
  }) });
@@ -879,7 +908,7 @@ function MultiSection({
879
908
  allClearLabels
880
909
  }) {
881
910
  const allSelected = section.value.length === section.options.length;
882
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
911
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
883
912
  section.options.map((option, i) => {
884
913
  const selected = section.value.includes(option.value);
885
914
  const rowColor = selected ? onAccentColor : mutedColor;
@@ -887,12 +916,12 @@ function MultiSection({
887
916
  const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
888
917
  const itemStyle = [styles8.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
889
918
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
890
- return /* @__PURE__ */ (0, import_jsx_runtime9.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_runtime9.jsxs)(import_react_native10.View, { style: styles8.itemRow, children: [
891
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper5.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
892
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper5.Text, { variant: "labelLarge", style: [styles8.itemLabelFlex, labelStyle], children: option.label })
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 })
893
922
  ] }) }, option.value);
894
923
  }),
895
- section.allClear && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles8.item, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper5.Text, { variant: "labelLarge", style: [styles8.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
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 }) })
896
925
  ] });
897
926
  }
898
927
  var styles8 = import_react_native10.StyleSheet.create({
@@ -965,21 +994,21 @@ var styles8 = import_react_native10.StyleSheet.create({
965
994
  });
966
995
 
967
996
  // src/ControlSchemePicker.tsx
968
- var import_jsx_runtime10 = require("react/jsx-runtime");
997
+ var import_jsx_runtime11 = require("react/jsx-runtime");
969
998
  function ControlSchemePicker({ id, host, value, onChange, options, takenValue, accentColor, mutedColor, onAccentColor, dark, align, labelFontFamily }) {
970
- const isTouchPrimary = (0, import_core4.useIsTouchPrimaryDevice)();
999
+ const isTouchPrimary = (0, import_core6.useIsTouchPrimaryDevice)();
971
1000
  if (import_react_native11.Platform.OS !== "web" || isTouchPrimary) return null;
972
- return /* @__PURE__ */ (0, import_jsx_runtime10.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 });
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 });
973
1002
  }
974
1003
 
975
1004
  // src/CornerActionButtons.tsx
976
1005
  var import_feedback_press5 = require("@rific/feedback-press");
977
1006
  var import_react_native12 = require("react-native");
978
- var import_jsx_runtime11 = require("react/jsx-runtime");
1007
+ var import_jsx_runtime12 = require("react/jsx-runtime");
979
1008
  function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon = "arrow-left", backLabel = "Back" }) {
980
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
981
- /* @__PURE__ */ (0, import_jsx_runtime11.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 }),
982
- /* @__PURE__ */ (0, import_jsx_runtime11.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" })
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" })
983
1012
  ] });
984
1013
  }
985
1014
  var styles9 = import_react_native12.StyleSheet.create({
@@ -994,17 +1023,17 @@ var styles9 = import_react_native12.StyleSheet.create({
994
1023
  // src/CornerStatusBadges.tsx
995
1024
  var import_react_native13 = require("react-native");
996
1025
  var import_react_native_paper6 = require("react-native-paper");
997
- var import_jsx_runtime12 = require("react/jsx-runtime");
1026
+ var import_jsx_runtime13 = require("react/jsx-runtime");
998
1027
  var BADGE_SIZE = 30;
999
1028
  function StatusBadge({ icon }) {
1000
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { style: styles10.badge, children: icon && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper6.Icon, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
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)" }) });
1001
1030
  }
1002
1031
  function CornerStatusBadges({ orientationMode, p1OnRight, p1, p2 }) {
1003
1032
  const p1OnRightSide = orientationMode === "sideBySide" && p1OnRight;
1004
1033
  const p2OnLeftSide = orientationMode === "sideBySide" && !p1OnRight;
1005
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1006
- p1 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { pointerEvents: "none", style: p1OnRightSide ? styles10.bottomRight : styles10.bottomLeft, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(StatusBadge, { icon: p1.icon }) }),
1007
- p2 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { pointerEvents: "none", style: p2OnLeftSide ? styles10.topLeft : styles10.topRight, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(StatusBadge, { icon: p2.icon }) })
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 }) })
1008
1037
  ] });
1009
1038
  }
1010
1039
  var styles10 = import_react_native13.StyleSheet.create({
@@ -1028,10 +1057,10 @@ var styles10 = import_react_native13.StyleSheet.create({
1028
1057
  // src/InlineColorPicker.tsx
1029
1058
  var import_auto_paper6 = require("@rific/auto-paper");
1030
1059
  var import_feedback_press6 = require("@rific/feedback-press");
1031
- var import_core5 = require("@tastic/core");
1060
+ var import_core7 = require("@tastic/core");
1032
1061
  var import_react_native14 = require("react-native");
1033
1062
  var import_react_native_paper7 = require("react-native-paper");
1034
- var import_jsx_runtime13 = require("react/jsx-runtime");
1063
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1035
1064
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
1036
1065
  var DEFAULT_SIZE = 48;
1037
1066
  var SWATCH_SIZE = 28;
@@ -1044,29 +1073,31 @@ var SCREEN_MARGIN = 40;
1044
1073
  function widthForColumns(columns) {
1045
1074
  return SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * columns + SWATCHES_GAP * (columns - 1);
1046
1075
  }
1047
- function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = import_auto_paper6.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;
1048
1079
  const menuBg = dark ? "#000000" : "#FFFFFF";
1049
1080
  const { width: windowWidth } = (0, import_react_native14.useWindowDimensions)();
1050
- const autoColumns = (0, import_core5.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
1081
+ const autoColumns = (0, import_core7.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
1051
1082
  const resolvedColumns = columns ?? autoColumns;
1052
1083
  const swatchesWidth = widthForColumns(resolvedColumns);
1053
1084
  const swatchRows = Math.ceil(swatches.length / resolvedColumns);
1054
1085
  const swatchesHeight = SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * swatchRows + SWATCHES_GAP * (swatchRows - 1);
1055
1086
  const open = host.openId === id;
1056
- const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, swatchesWidth, swatchesHeight);
1087
+ const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, swatchesWidth, swatchesHeight, rotation);
1057
1088
  const align = alignOverride ?? autoAlign;
1058
1089
  const displayFor = previewValue ?? ((hex) => hex);
1059
1090
  const displayValue = displayFor(value);
1060
1091
  const contrastColor = (0, import_auto_paper6.getContrastColor)(displayValue);
1061
1092
  const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
1062
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native14.View, { style: [styles11.anchor, open && styles11.anchorOpen], children: [
1063
- /* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)(import_react_native14.View, { ref: triggerRef, collapsable: false, style: styles11.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)(import_react_native_paper7.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
1064
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native14.ScrollView, { style: [styles11.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles11.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) => {
1065
1096
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
1066
1097
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
1067
1098
  const swappable = taken && allowSwapTaken;
1068
1099
  const swatchDisplay = displayFor(swatch.value);
1069
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1100
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1070
1101
  import_feedback_press6.TouchableRipple,
1071
1102
  {
1072
1103
  disabled: taken && !swappable,
@@ -1076,7 +1107,7 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1076
1107
  },
1077
1108
  borderless: true,
1078
1109
  style: [styles11.swatch, { backgroundColor: swatchDisplay }, selected && styles11.swatchSelected, taken && !swappable && styles11.swatchTaken],
1079
- children: selected ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper7.Icon, { source: "check", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper7.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper7.Icon, { source: "close", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native14.View, {})
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, {})
1080
1111
  },
1081
1112
  swatch.value
1082
1113
  );
@@ -1159,7 +1190,7 @@ var import_auto_paper7 = require("@rific/auto-paper");
1159
1190
  var import_feedback_press7 = require("@rific/feedback-press");
1160
1191
  var import_react_native15 = require("react-native");
1161
1192
  var import_react_native_paper8 = require("react-native-paper");
1162
- var import_jsx_runtime14 = require("react/jsx-runtime");
1193
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1163
1194
  var TRIGGER_HEIGHT = 28;
1164
1195
  var POPOVER_WIDTH = 180;
1165
1196
  var ROW_HEIGHT = 36;
@@ -1182,17 +1213,17 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
1182
1213
  onChange(option.value);
1183
1214
  host.close();
1184
1215
  };
1185
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native15.View, { style: [styles12.anchor, open && styles12.anchorOpen], children: [
1186
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => host.toggle(id), style: styles12.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native15.View, { ref: triggerRef, collapsable: false, style: styles12.triggerInner, children: [
1187
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Text, { style: [styles12.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
1188
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.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 })
1189
1220
  ] }) }),
1190
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime14.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) => {
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) => {
1191
1222
  const isSelected = option.value === value;
1192
1223
  const onColor = (0, import_auto_paper7.getContrastColor)(color);
1193
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => handleSelect(option), style: [styles12.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native15.View, { style: styles12.rowInner, children: [
1194
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
1195
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Text, { style: [styles12.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
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 })
1196
1227
  ] }) }, option.value);
1197
1228
  }) }) })
1198
1229
  ] });
@@ -1249,24 +1280,29 @@ var styles12 = import_react_native15.StyleSheet.create({
1249
1280
  }
1250
1281
  });
1251
1282
 
1283
+ // src/loadSkiaWeb.ts
1284
+ function loadSkiaWeb() {
1285
+ return Promise.resolve();
1286
+ }
1287
+
1252
1288
  // src/PressAwayOverlay.tsx
1253
1289
  var import_react_native16 = require("react-native");
1254
- var import_jsx_runtime15 = require("react/jsx-runtime");
1290
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1255
1291
  function PressAwayOverlay({ active, onPress, style }) {
1256
1292
  if (!active) return null;
1257
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native16.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native16.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] });
1258
1294
  }
1259
1295
 
1260
1296
  // src/ReadyButton.tsx
1261
1297
  var import_feedback_press8 = require("@rific/feedback-press");
1262
1298
  var import_react_native17 = require("react-native");
1263
1299
  var import_react_native_paper9 = require("react-native-paper");
1264
- var import_jsx_runtime16 = require("react/jsx-runtime");
1300
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1265
1301
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
1266
1302
  const readyButtonStyle = [styles13.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
1267
1303
  const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
1268
1304
  const labelColorStyle = { color: ready ? "#000000" : color };
1269
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_feedback_press8.TouchableRipple, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper9.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?" }) });
1270
1306
  }
1271
1307
  var styles13 = import_react_native17.StyleSheet.create({
1272
1308
  readyButton: {
@@ -1283,14 +1319,14 @@ var styles13 = import_react_native17.StyleSheet.create({
1283
1319
  // src/SharedActionBand.tsx
1284
1320
  var import_feedback_press9 = require("@rific/feedback-press");
1285
1321
  var import_react_native18 = require("react-native");
1286
- var import_jsx_runtime17 = require("react/jsx-runtime");
1322
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1287
1323
  function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
1288
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_native18.View, { style: [styles14.column, popoverOpen && styles14.columnOpen], children: [
1289
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_native18.View, { style: styles14.actionsRow, children: [
1290
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_feedback_press9.IconButton, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1291
- onRandomize && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_feedback_press9.IconButton, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1292
- onReset && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_feedback_press9.IconButton, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
1293
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_feedback_press9.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" })
1294
1330
  ] }),
1295
1331
  children
1296
1332
  ] });
@@ -1314,14 +1350,14 @@ var styles14 = import_react_native18.StyleSheet.create({
1314
1350
  var import_auto_paper8 = require("@rific/auto-paper");
1315
1351
  var import_react_native19 = require("react-native");
1316
1352
  var import_react_native_paper10 = require("react-native-paper");
1317
- var import_jsx_runtime18 = require("react/jsx-runtime");
1353
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1318
1354
  function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }) {
1319
1355
  const { dark } = (0, import_auto_paper8.useAutoPaperTheme)();
1320
1356
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
1321
1357
  const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
1322
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native19.View, { style: styles15.row, children: [
1323
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native_paper10.Text, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
1324
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native_paper10.Text, { variant: "bodyMedium", style: [styles15.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 })
1325
1361
  ] });
1326
1362
  }
1327
1363
  var styles15 = import_react_native19.StyleSheet.create({
@@ -1339,13 +1375,13 @@ var styles15 = import_react_native19.StyleSheet.create({
1339
1375
  var import_auto_paper9 = require("@rific/auto-paper");
1340
1376
  var import_react_native20 = require("react-native");
1341
1377
  var import_react_native_paper11 = require("react-native-paper");
1342
- var import_jsx_runtime19 = require("react/jsx-runtime");
1378
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1343
1379
  function StatSection({ label, fg: fgOverride, sectionBg: sectionBgOverride, children }) {
1344
1380
  const { dark } = (0, import_auto_paper9.useAutoPaperTheme)();
1345
1381
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
1346
1382
  const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
1347
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native20.View, { style: [styles16.section, { backgroundColor: sectionBg }], children: [
1348
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Text, { variant: "labelMedium", style: [styles16.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 }),
1349
1385
  children
1350
1386
  ] });
1351
1387
  }
@@ -1395,6 +1431,7 @@ function usePopoverHost() {
1395
1431
  ReadyButton,
1396
1432
  SectionedDropdown,
1397
1433
  SharedActionBand,
1434
+ SkiaGate,
1398
1435
  StatRow,
1399
1436
  StatSection,
1400
1437
  TriggerGaugeHost,