@tastic/hud 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -38,22 +38,22 @@ __export(TriggerGauge_exports, {
38
38
  function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, dashWidth, startDeg = DEFAULT_START_DEG, endDeg = startDeg }) {
39
39
  const totalDurationMs = MOUNT_DURATION_MS + Math.max(0, segments - 1) * MOUNT_STAGGER_MS;
40
40
  const mountProgress = (0, import_react_native_reanimated.useSharedValue)(0);
41
- (0, import_react6.useLayoutEffect)(() => {
41
+ (0, import_react7.useLayoutEffect)(() => {
42
42
  mountProgress.value = (0, import_react_native_reanimated.withTiming)(1, { duration: totalDurationMs, easing: import_react_native_reanimated.Easing.out(import_react_native_reanimated.Easing.cubic) });
43
43
  }, []);
44
44
  const rawSweepDeg = ((endDeg - startDeg) % 360 + 360) % 360;
45
45
  const totalSweepDeg = rawSweepDeg === 0 ? 360 : rawSweepDeg;
46
46
  const step = segments > 0 ? totalSweepDeg / segments : 0;
47
- const centerDeg = (0, import_react6.useCallback)((i) => startDeg + (i + 0.5) * step, [startDeg, step]);
47
+ const centerDeg = (0, import_react7.useCallback)((i) => startDeg + (i + 0.5) * step, [startDeg, step]);
48
48
  const singleSelect = litIndices.length === 1;
49
49
  const angleDeg = (0, import_react_native_reanimated.useSharedValue)(centerDeg(litIndices[0] ?? 0));
50
50
  const litIndicesKey = [...litIndices].sort((a, b) => a - b).join(",");
51
- const [transitionFrom, setTransitionFrom] = (0, import_react6.useState)(litIndices);
51
+ const [transitionFrom, setTransitionFrom] = (0, import_react7.useState)(litIndices);
52
52
  const transitionFromKey = [...transitionFrom].sort((a, b) => a - b).join(",");
53
- const lastKeyRef = (0, import_react6.useRef)(litIndicesKey);
54
- const lastIndicesRef = (0, import_react6.useRef)(litIndices);
53
+ const lastKeyRef = (0, import_react7.useRef)(litIndicesKey);
54
+ const lastIndicesRef = (0, import_react7.useRef)(litIndices);
55
55
  const selectionProgress = (0, import_react_native_reanimated.useSharedValue)(1);
56
- (0, import_react6.useLayoutEffect)(() => {
56
+ (0, import_react7.useLayoutEffect)(() => {
57
57
  if (litIndicesKey !== lastKeyRef.current) {
58
58
  if (singleSelect && lastIndicesRef.current.length === 1) {
59
59
  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) });
@@ -82,7 +82,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
82
82
  if (mountProgress.value < 1) {
83
83
  const startFrac = i * MOUNT_STAGGER_MS / totalDurationMs;
84
84
  const endFrac = startFrac + MOUNT_DURATION_MS / totalDurationMs;
85
- local = (0, import_core5.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
85
+ local = (0, import_core6.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
86
86
  }
87
87
  const sweep = dashAngleDeg * local;
88
88
  if (sweep <= 0) continue;
@@ -106,7 +106,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
106
106
  if (!isLitNow) continue;
107
107
  const startFrac = i * MOUNT_STAGGER_MS / totalDurationMs;
108
108
  const endFrac = startFrac + MOUNT_DURATION_MS / totalDurationMs;
109
- local = (0, import_core5.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
109
+ local = (0, import_core6.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
110
110
  } else if (isLitNow === wasLit) {
111
111
  local = 1;
112
112
  } else if (isLitNow) {
@@ -130,21 +130,21 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
130
130
  height: canvasSize,
131
131
  width: canvasSize
132
132
  };
133
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native13.View, { style: [styles11.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
134
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_skia.Path, { path: unlitPath, style: "stroke", strokeWidth: UNLIT_THICKNESS, strokeCap: "round", color: mutedColor, opacity: 0.35 }),
135
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_skia.Path, { path: litPath, style: "stroke", strokeWidth: LIT_THICKNESS, strokeCap: "round", color: accentColor, opacity: 1 })
133
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native14.View, { style: [styles12.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
134
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_skia.Path, { path: unlitPath, style: "stroke", strokeWidth: UNLIT_THICKNESS, strokeCap: "round", color: mutedColor, opacity: 0.35 }),
135
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_skia.Path, { path: litPath, style: "stroke", strokeWidth: LIT_THICKNESS, strokeCap: "round", color: accentColor, opacity: 1 })
136
136
  ] }) });
137
137
  }
138
- var import_react_native_skia, import_core5, import_react6, import_react_native13, import_react_native_reanimated, import_jsx_runtime13, 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, styles11;
138
+ var import_react_native_skia, import_core6, import_react7, import_react_native14, import_react_native_reanimated, import_jsx_runtime14, 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, styles12;
139
139
  var init_TriggerGauge = __esm({
140
140
  "src/TriggerGauge.tsx"() {
141
141
  "use strict";
142
142
  import_react_native_skia = require("@shopify/react-native-skia");
143
- import_core5 = require("@tastic/core");
144
- import_react6 = require("react");
145
- import_react_native13 = require("react-native");
143
+ import_core6 = require("@tastic/core");
144
+ import_react7 = require("react");
145
+ import_react_native14 = require("react-native");
146
146
  import_react_native_reanimated = require("react-native-reanimated");
147
- import_jsx_runtime13 = require("react/jsx-runtime");
147
+ import_jsx_runtime14 = require("react/jsx-runtime");
148
148
  UNLIT_THICKNESS = 3;
149
149
  LIT_THICKNESS = 7;
150
150
  DASH_GAP_PX = 9;
@@ -154,7 +154,7 @@ var init_TriggerGauge = __esm({
154
154
  MOUNT_DURATION_MS = 450;
155
155
  MOUNT_STAGGER_MS = 40;
156
156
  SELECTION_TRANSITION_DURATION_MS = 260;
157
- styles11 = import_react_native13.StyleSheet.create({
157
+ styles12 = import_react_native14.StyleSheet.create({
158
158
  container: {
159
159
  alignItems: "center",
160
160
  justifyContent: "center",
@@ -187,6 +187,7 @@ __export(index_exports, {
187
187
  PopoverBody: () => PopoverBody,
188
188
  PressAwayOverlay: () => PressAwayOverlay,
189
189
  ReadyButton: () => ReadyButton,
190
+ RotatedFrame: () => RotatedFrame,
190
191
  SectionedDropdown: () => SectionedDropdown,
191
192
  SharedActionBand: () => SharedActionBand,
192
193
  SkiaGate: () => SkiaGate,
@@ -419,10 +420,10 @@ function ActivityStatSection({ stats }) {
419
420
  var import_auto_paper7 = require("@rific/auto-paper");
420
421
  var import_feedback_press2 = require("@rific/feedback-press");
421
422
  var import_updater = require("@rific/updater");
422
- var import_core2 = require("@tastic/core");
423
- var import_react3 = require("react");
424
- var import_react_native9 = require("react-native");
425
- var import_react_native_paper7 = require("react-native-paper");
423
+ var import_core3 = require("@tastic/core");
424
+ var import_react4 = require("react");
425
+ var import_react_native10 = require("react-native");
426
+ var import_react_native_paper8 = require("react-native-paper");
426
427
 
427
428
  // src/ConfirmDialog.tsx
428
429
  var import_auto_paper6 = require("@rific/auto-paper");
@@ -550,19 +551,49 @@ var styles6 = import_react_native8.StyleSheet.create({
550
551
  }
551
552
  });
552
553
 
553
- // src/useUpdateConfirmBridge.ts
554
+ // src/RotatedFrame.tsx
555
+ var import_core2 = require("@tastic/core");
554
556
  var import_react2 = require("react");
557
+ var import_react_native9 = require("react-native");
558
+ var import_react_native_paper7 = require("react-native-paper");
559
+ var import_react_native_safe_area_context = require("react-native-safe-area-context");
560
+ var import_jsx_runtime8 = require("react/jsx-runtime");
561
+ function RotatedFrame({ children, rotation: rotationOverride, rotatedSafeArea = false, portalHost = false, passThrough = false, maxContentWidth }) {
562
+ const ambientRotation = (0, import_core2.useRotation)();
563
+ const rotation = rotationOverride ?? ambientRotation;
564
+ const window = (0, import_react_native9.useWindowDimensions)();
565
+ const { width, height } = (0, import_core2.rotateDimensions)(window.width, window.height, rotation);
566
+ const rawInsets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
567
+ const insets = (0, import_react2.useMemo)(() => rotatedSafeArea ? (0, import_core2.rotateInsets)(rawInsets, rotation) : rawInsets, [rotatedSafeArea, rawInsets, rotation]);
568
+ const cappedWidth = maxContentWidth !== void 0 && width > height ? maxContentWidth : void 0;
569
+ const column = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native9.View, { style: [styles7.column, cappedWidth !== void 0 && { maxWidth: cappedWidth }], pointerEvents: passThrough ? "box-none" : void 0, children });
570
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_core2.FakeLandscapeView, { rotation, passThrough, style: styles7.root, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_safe_area_context.SafeAreaInsetsContext.Provider, { value: insets, children: portalHost ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Portal.Host, { children: column }) : column }) });
571
+ }
572
+ var styles7 = import_react_native9.StyleSheet.create({
573
+ // A centred column: full width up to `maxWidth`, so a capped frame sits in the middle of a wider window.
574
+ column: {
575
+ alignSelf: "center",
576
+ flex: 1,
577
+ width: "100%"
578
+ },
579
+ root: {
580
+ flex: 1
581
+ }
582
+ });
583
+
584
+ // src/useUpdateConfirmBridge.ts
585
+ var import_react3 = require("react");
555
586
  function useUpdateConfirmBridge() {
556
- const [manifest, setManifest] = (0, import_react2.useState)(null);
557
- const resolveRef = (0, import_react2.useRef)(null);
558
- const onConfirm = (0, import_react2.useCallback)(
587
+ const [manifest, setManifest] = (0, import_react3.useState)(null);
588
+ const resolveRef = (0, import_react3.useRef)(null);
589
+ const onConfirm = (0, import_react3.useCallback)(
559
590
  (next) => new Promise((resolve) => {
560
591
  resolveRef.current = resolve;
561
592
  setManifest(next);
562
593
  }),
563
594
  []
564
595
  );
565
- const respond = (0, import_react2.useCallback)((confirmed) => {
596
+ const respond = (0, import_react3.useCallback)((confirmed) => {
566
597
  setManifest(null);
567
598
  resolveRef.current?.(confirmed);
568
599
  resolveRef.current = null;
@@ -571,20 +602,20 @@ function useUpdateConfirmBridge() {
571
602
  }
572
603
 
573
604
  // src/BaseSettingsDialog.tsx
574
- var import_jsx_runtime8 = require("react/jsx-runtime");
605
+ var import_jsx_runtime9 = require("react/jsx-runtime");
575
606
  function SettingIcon({ source, color, containerColor }) {
576
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native9.View, { style: [styles7.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Icon, { source, size: 18, color }) });
607
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: [styles8.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Icon, { source, size: 18, color }) });
577
608
  }
578
609
  function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, onShowHowToPlay, children }) {
579
610
  const { colors } = (0, import_auto_paper7.useAutoPaperTheme)();
580
- const ambientRotation = (0, import_core2.useRotation)();
611
+ const ambientRotation = (0, import_core3.useRotation)();
581
612
  const rotation = rotationOverride ?? ambientRotation;
582
- const isTouchPrimary = (0, import_core2.useIsTouchPrimaryDevice)();
613
+ const isTouchPrimary = (0, import_core3.useIsTouchPrimaryDevice)();
583
614
  const { settings: hapticSettings, set: setHapticSettings } = (0, import_feedback_press2.useHapticSettings)();
584
615
  const { settings: soundSettings, set: setSoundSettings } = (0, import_feedback_press2.useSoundSettings)();
585
- const sound = (0, import_react3.useContext)(import_feedback_press2.SoundContext);
616
+ const sound = (0, import_react4.useContext)(import_feedback_press2.SoundContext);
586
617
  const { forceShort: forceHapticFeedback } = (0, import_feedback_press2.useVibration)();
587
- const [infoMessage, setInfoMessage] = (0, import_react3.useState)(null);
618
+ const [infoMessage, setInfoMessage] = (0, import_react4.useState)(null);
588
619
  const { manifest: updateManifest, onConfirm: onUpdateConfirm, respond: respondToUpdateConfirm } = useUpdateConfirmBridge();
589
620
  const { check, checking, updateReady } = (0, import_updater.useUpdater)({
590
621
  autoCheck: false,
@@ -595,32 +626,32 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
595
626
  });
596
627
  const updateManifestDate = updateManifest ? new Date(updateManifest.createdAt) : null;
597
628
  const showLockOrientation = isTouchPrimary && lockOrientation !== void 0 && onLockOrientationChange !== void 0;
598
- const showEdgeGuard = import_react_native9.Platform.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
599
- const showHaptics = import_react_native9.Platform.OS !== "web" && !hideHaptics;
629
+ const showEdgeGuard = import_react_native10.Platform.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
630
+ const showHaptics = import_react_native10.Platform.OS !== "web" && !hideHaptics;
600
631
  const showSound = !hideSound;
601
632
  const showAppearance = !hideAppearance;
602
- const showUpdateCheck = import_react_native9.Platform.OS !== "web" && !hideUpdateCheck;
603
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
604
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_auto_paper7.Dialog, { visible, onDismiss, style: [styles7.dialog, (0, import_core2.toRotationStyle)(rotation)], children: [
605
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper7.Dialog.Title, { children: "Settings" }),
606
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper7.Dialog.ScrollArea, { style: styles7.scrollArea, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.ScrollView, { contentContainerStyle: styles7.content, showsVerticalScrollIndicator: false, children: [
607
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleGroup, children: [
608
- showLockOrientation && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles7.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleContent, children: [
609
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
610
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.flexShrink, children: [
611
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
612
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
633
+ const showUpdateCheck = import_react_native10.Platform.OS !== "web" && !hideUpdateCheck;
634
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(RotatedFrame, { rotation, rotatedSafeArea: true, portalHost: true, passThrough: true, children: [
635
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_auto_paper7.Dialog, { visible, onDismiss, style: styles8.dialog, children: [
636
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper7.Dialog.Title, { children: "Settings" }),
637
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper7.Dialog.ScrollArea, { style: styles8.scrollArea, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.ScrollView, { contentContainerStyle: styles8.content, showsVerticalScrollIndicator: false, children: [
638
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleGroup, children: [
639
+ showLockOrientation && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles8.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleContent, children: [
640
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
641
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.flexShrink, children: [
642
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
643
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
613
644
  ] })
614
645
  ] }) }),
615
- showEdgeGuard && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles7.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleContent, children: [
616
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
617
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.flexShrink, children: [
618
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
619
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
646
+ showEdgeGuard && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles8.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleContent, children: [
647
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.flexShrink, children: [
649
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
650
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
620
651
  ] })
621
652
  ] }) }),
622
- (showSound || showHaptics) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleRow, children: [
623
- showSound && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
653
+ (showSound || showHaptics) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleRow, children: [
654
+ showSound && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
624
655
  import_feedback_press2.TouchableRipple,
625
656
  {
626
657
  soundDisabled: true,
@@ -629,15 +660,15 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
629
660
  setSoundSettings({ enabled });
630
661
  if (enabled) sound.selection?.();
631
662
  },
632
- style: styles7.toggleButtonInRow,
663
+ style: styles8.toggleButtonInRow,
633
664
  accessibilityLabel: `Sound ${soundSettings.enabled ? "on" : "off"}`,
634
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleContent, children: [
635
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
636
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
665
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleContent, children: [
666
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
667
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
637
668
  ] })
638
669
  }
639
670
  ),
640
- showHaptics && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
671
+ showHaptics && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
641
672
  import_feedback_press2.TouchableRipple,
642
673
  {
643
674
  hapticDisabled: true,
@@ -646,54 +677,54 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
646
677
  setHapticSettings({ vibrate });
647
678
  if (vibrate) forceHapticFeedback();
648
679
  },
649
- style: styles7.toggleButtonInRow,
680
+ style: styles8.toggleButtonInRow,
650
681
  accessibilityLabel: `Haptics ${hapticSettings.vibrate ? "on" : "off"}`,
651
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleContent, children: [
652
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
653
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
682
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleContent, children: [
683
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
684
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
654
685
  ] })
655
686
  }
656
687
  )
657
688
  ] }),
658
- onShowHowToPlay && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
689
+ onShowHowToPlay && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
659
690
  import_feedback_press2.TouchableRipple,
660
691
  {
661
692
  onPress: () => {
662
693
  onDismiss();
663
694
  onShowHowToPlay();
664
695
  },
665
- style: styles7.toggleButton,
696
+ style: styles8.toggleButton,
666
697
  accessibilityLabel: "How to Play",
667
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleContent, children: [
668
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SettingIcon, { source: "help-circle-outline", color: colors.secondary, containerColor: colors.secondaryContainer }),
669
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.flexShrink, children: [
670
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "How to Play" }),
671
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Replay the quick intro" })
698
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleContent, children: [
699
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SettingIcon, { source: "help-circle-outline", color: colors.secondary, containerColor: colors.secondaryContainer }),
700
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.flexShrink, children: [
701
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "How to Play" }),
702
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Replay the quick intro" })
672
703
  ] })
673
704
  ] })
674
705
  }
675
706
  )
676
707
  ] }),
677
- showAppearance && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.section, children: [
678
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "labelMedium", style: [styles7.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
679
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper7.AutoAppearancePicker, { showLabels: false })
708
+ showAppearance && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.section, children: [
709
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "labelMedium", style: [styles8.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
710
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper7.AutoAppearancePicker, { showLabels: false })
680
711
  ] }),
681
712
  children,
682
- showUpdateCheck && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.section, children: [
683
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native_paper7.Text, { variant: "labelSmall", style: [styles7.sectionLabel, { color: colors.onSurfaceVariant }], children: [
713
+ showUpdateCheck && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.section, children: [
714
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_paper8.Text, { variant: "labelSmall", style: [styles8.sectionLabel, { color: colors.onSurfaceVariant }], children: [
684
715
  "VERSION ",
685
716
  version,
686
717
  updateReady ? " \xB7 UPDATE READY" : ""
687
718
  ] }),
688
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press2.Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
719
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press2.Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
689
720
  ] })
690
721
  ] }) })
691
722
  ] }),
692
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ConfirmDialog, { visible: !!updateManifest, title: "Update Available", message: updateManifestDate ? `Released ${updateManifestDate.toLocaleDateString()} at ${updateManifestDate.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation, onConfirm: () => respondToUpdateConfirm(true), onCancel: () => respondToUpdateConfirm(false) }),
693
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ConfirmDialog, { visible: !!infoMessage, title: infoMessage?.title ?? "", message: infoMessage?.message ?? "", confirmLabel: "OK", icon: "information-outline", rotation, onConfirm: () => setInfoMessage(null) })
694
- ] });
723
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ConfirmDialog, { visible: !!updateManifest, title: "Update Available", message: updateManifestDate ? `Released ${updateManifestDate.toLocaleDateString()} at ${updateManifestDate.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation: 0, onConfirm: () => respondToUpdateConfirm(true), onCancel: () => respondToUpdateConfirm(false) }),
724
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ConfirmDialog, { visible: !!infoMessage, title: infoMessage?.title ?? "", message: infoMessage?.message ?? "", confirmLabel: "OK", icon: "information-outline", rotation: 0, onConfirm: () => setInfoMessage(null) })
725
+ ] }) });
695
726
  }
696
- var styles7 = import_react_native9.StyleSheet.create({
727
+ var styles8 = import_react_native10.StyleSheet.create({
697
728
  content: {
698
729
  gap: 24,
699
730
  paddingVertical: 20
@@ -763,29 +794,29 @@ var styles7 = import_react_native9.StyleSheet.create({
763
794
  // src/BaseStatsScreen.tsx
764
795
  var import_auto_paper8 = require("@rific/auto-paper");
765
796
  var import_feedback_press3 = require("@rific/feedback-press");
766
- var import_core3 = require("@tastic/core");
767
- var import_react4 = require("react");
768
- var import_react_native10 = require("react-native");
769
- var import_react_native_paper8 = require("react-native-paper");
770
- var import_jsx_runtime9 = require("react/jsx-runtime");
797
+ var import_core4 = require("@tastic/core");
798
+ var import_react5 = require("react");
799
+ var import_react_native11 = require("react-native");
800
+ var import_react_native_paper9 = require("react-native-paper");
801
+ var import_jsx_runtime10 = require("react/jsx-runtime");
771
802
  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, children }) {
772
803
  const { dark, colors } = (0, import_auto_paper8.useAutoPaperTheme)();
773
- const ambientRotation = (0, import_core3.useRotation)();
804
+ const ambientRotation = (0, import_core4.useRotation)();
774
805
  const rotation = rotationOverride ?? ambientRotation;
775
- const [confirmResetVisible, setConfirmResetVisible] = (0, import_react4.useState)(false);
806
+ const [confirmResetVisible, setConfirmResetVisible] = (0, import_react5.useState)(false);
776
807
  const showReset = !!onReset;
777
808
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
778
809
  const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
779
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: [styles8.container, { backgroundColor: bg }], children: [
780
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: [styles8.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
781
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
782
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: titleVariant, style: [styles8.title, { color: fg }], children: title })
810
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: [styles9.container, { backgroundColor: bg }], children: [
811
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: [styles9.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
812
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
813
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper9.Text, { variant: titleVariant, style: [styles9.title, { color: fg }], children: title })
783
814
  ] }),
784
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.ScrollView, { style: styles8.scrollView, contentContainerStyle: [styles8.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
815
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.ScrollView, { style: styles9.scrollView, contentContainerStyle: [styles9.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
785
816
  children,
786
- showReset && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press3.Button, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles8.resetButton, children: resetLabel })
817
+ showReset && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press3.Button, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles9.resetButton, children: resetLabel })
787
818
  ] }),
788
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
819
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
789
820
  ConfirmDialog,
790
821
  {
791
822
  visible: confirmResetVisible,
@@ -805,7 +836,7 @@ function BaseStatsScreen({ rotation: rotationOverride, title = "Achievements", t
805
836
  )
806
837
  ] });
807
838
  }
808
- var styles8 = import_react_native10.StyleSheet.create({
839
+ var styles9 = import_react_native11.StyleSheet.create({
809
840
  container: {
810
841
  flex: 1
811
842
  },
@@ -836,19 +867,19 @@ var styles8 = import_react_native10.StyleSheet.create({
836
867
  });
837
868
 
838
869
  // src/ContentGutter.tsx
839
- var import_core4 = require("@tastic/core");
840
- var import_react_native11 = require("react-native");
841
- var import_jsx_runtime10 = require("react/jsx-runtime");
870
+ var import_core5 = require("@tastic/core");
871
+ var import_react_native12 = require("react-native");
872
+ var import_jsx_runtime11 = require("react/jsx-runtime");
842
873
  function ContentGutter({ maxContentWidth, leftGutter, rightGutter, style, children }) {
843
- const { width: windowWidth } = (0, import_core4.useRotatedWindowDimensions)();
844
- const { contentWidth, gutterWidth } = (0, import_core4.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
845
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: [styles9.row, style], children: [
846
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native11.View, { style: [styles9.gutter, { width: gutterWidth }], children: leftGutter }),
847
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native11.View, { style: { width: contentWidth }, children }),
848
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native11.View, { style: [styles9.gutter, { width: gutterWidth }], children: rightGutter })
874
+ const { width: windowWidth } = (0, import_core5.useRotatedWindowDimensions)();
875
+ const { contentWidth, gutterWidth } = (0, import_core5.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
876
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native12.View, { style: [styles10.row, style], children: [
877
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native12.View, { style: [styles10.gutter, { width: gutterWidth }], children: leftGutter }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native12.View, { style: { width: contentWidth }, children }),
879
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native12.View, { style: [styles10.gutter, { width: gutterWidth }], children: rightGutter })
849
880
  ] });
850
881
  }
851
- var styles9 = import_react_native11.StyleSheet.create({
882
+ var styles10 = import_react_native12.StyleSheet.create({
852
883
  // overflow:'hidden' keeps an oversized leftGutter/rightGutter from bleeding into the content
853
884
  // column instead of clipping cleanly at its own real width.
854
885
  gutter: {
@@ -860,41 +891,41 @@ var styles9 = import_react_native11.StyleSheet.create({
860
891
  });
861
892
 
862
893
  // src/ControlSchemePicker.tsx
863
- var import_core7 = require("@tastic/core");
864
- var import_react_native16 = require("react-native");
894
+ var import_core8 = require("@tastic/core");
895
+ var import_react_native17 = require("react-native");
865
896
 
866
897
  // src/SectionedDropdown.tsx
867
898
  var import_auto_paper9 = require("@rific/auto-paper");
868
899
  var import_feedback_press4 = require("@rific/feedback-press");
869
- var import_core6 = require("@tastic/core");
870
- var import_react_native15 = require("react-native");
871
- var import_react_native_paper9 = require("react-native-paper");
900
+ var import_core7 = require("@tastic/core");
901
+ var import_react_native16 = require("react-native");
902
+ var import_react_native_paper10 = require("react-native-paper");
872
903
 
873
904
  // src/PopoverBody.tsx
874
- var import_react_native12 = require("react-native");
875
- var import_jsx_runtime11 = require("react/jsx-runtime");
905
+ var import_react_native13 = require("react-native");
906
+ var import_jsx_runtime12 = require("react/jsx-runtime");
876
907
  var CARET_SIZE = 7;
877
908
  var CARET_RING_OFFSET = 2;
878
909
  var CARET_DIP = 1;
879
910
  function PopoverBody({ visible, children, align = "center", verticalAlign = "below", caretColor, caretBorderColor, caretBorderWidth = 1, triggerSize = 0 }) {
880
911
  if (!visible) return null;
881
912
  const above = verticalAlign === "above";
882
- const alignStyle = align === "left" ? styles10.contentLeft : align === "right" ? styles10.contentRight : styles10.contentCenter;
883
- const verticalStyle = above ? styles10.contentAbove : styles10.contentBelow;
913
+ const alignStyle = align === "left" ? styles11.contentLeft : align === "right" ? styles11.contentRight : styles11.contentCenter;
914
+ const verticalStyle = above ? styles11.contentAbove : styles11.contentBelow;
884
915
  const ringSize = CARET_SIZE + caretBorderWidth;
885
916
  const caretOffset = (halfFootprint) => align === "right" ? { right: triggerSize / 2 - halfFootprint } : { left: triggerSize / 2 - halfFootprint };
886
917
  const caretFill = above ? "borderTopColor" : "borderBottomColor";
887
918
  const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
888
919
  const caretEdge = (size) => above ? { bottom: -size + CARET_DIP } : { top: -size + CARET_DIP };
889
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native12.View, { style: [styles10.content, alignStyle, verticalStyle], children: [
920
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native13.View, { style: [styles11.content, alignStyle, verticalStyle], children: [
890
921
  children,
891
- caretColor && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
892
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native12.View, { style: [styles10.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
893
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native12.View, { style: [styles10.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
922
+ caretColor && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
923
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { style: [styles11.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
924
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { style: [styles11.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
894
925
  ] })
895
926
  ] });
896
927
  }
897
- var styles10 = import_react_native12.StyleSheet.create({
928
+ var styles11 = import_react_native13.StyleSheet.create({
898
929
  caret: {
899
930
  borderLeftColor: "transparent",
900
931
  borderRightColor: "transparent",
@@ -937,11 +968,11 @@ var styles10 = import_react_native12.StyleSheet.create({
937
968
  });
938
969
 
939
970
  // src/SkiaGate.tsx
940
- var import_react5 = require("react");
941
- var import_jsx_runtime12 = require("react/jsx-runtime");
971
+ var import_react6 = require("react");
972
+ var import_jsx_runtime13 = require("react/jsx-runtime");
942
973
  function SkiaGate({ getComponent, componentProps, fallback = null }) {
943
- const [Component, setComponent] = (0, import_react5.useState)(null);
944
- (0, import_react5.useEffect)(() => {
974
+ const [Component, setComponent] = (0, import_react6.useState)(null);
975
+ (0, import_react6.useEffect)(() => {
945
976
  let cancelled = false;
946
977
  getComponent().then((mod) => {
947
978
  if (!cancelled) setComponent(() => mod.default);
@@ -951,19 +982,19 @@ function SkiaGate({ getComponent, componentProps, fallback = null }) {
951
982
  };
952
983
  }, [getComponent]);
953
984
  if (!Component) return fallback;
954
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Component, { ...componentProps });
985
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Component, { ...componentProps });
955
986
  }
956
987
 
957
988
  // src/TriggerGaugeHost.tsx
958
- var import_jsx_runtime14 = require("react/jsx-runtime");
989
+ var import_jsx_runtime15 = require("react/jsx-runtime");
959
990
  var getTriggerGauge = () => Promise.resolve().then(() => (init_TriggerGauge(), TriggerGauge_exports)).then((mod) => ({ default: mod.TriggerGauge }));
960
991
  function TriggerGaugeHost(props) {
961
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
992
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
962
993
  }
963
994
 
964
995
  // src/useAutoAlign.ts
965
- var import_react7 = require("react");
966
- var import_react_native14 = require("react-native");
996
+ var import_react8 = require("react");
997
+ var import_react_native15 = require("react-native");
967
998
  var EDGE_MARGIN = 12;
968
999
  function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
969
1000
  if (rotation === 0) return { x, y, width, height };
@@ -975,18 +1006,18 @@ function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
975
1006
  return { x: cx + newDx - newWidth / 2, y: cy + newDy - newHeight / 2, width: newWidth, height: newHeight };
976
1007
  }
977
1008
  function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
978
- const triggerRef = (0, import_react7.useRef)(null);
979
- const { width: windowWidth, height: windowHeight } = (0, import_react_native14.useWindowDimensions)();
980
- const [align, setAlign] = (0, import_react7.useState)("center");
981
- const [verticalAlign, setVerticalAlign] = (0, import_react7.useState)("below");
982
- const [maxHeight, setMaxHeight] = (0, import_react7.useState)(contentHeight);
983
- const [measured, setMeasured] = (0, import_react7.useState)(false);
984
- const [prevOpen, setPrevOpen] = (0, import_react7.useState)(open);
1009
+ const triggerRef = (0, import_react8.useRef)(null);
1010
+ const { width: windowWidth, height: windowHeight } = (0, import_react_native15.useWindowDimensions)();
1011
+ const [align, setAlign] = (0, import_react8.useState)("center");
1012
+ const [verticalAlign, setVerticalAlign] = (0, import_react8.useState)("below");
1013
+ const [maxHeight, setMaxHeight] = (0, import_react8.useState)(contentHeight);
1014
+ const [measured, setMeasured] = (0, import_react8.useState)(false);
1015
+ const [prevOpen, setPrevOpen] = (0, import_react8.useState)(open);
985
1016
  if (open !== prevOpen) {
986
1017
  setPrevOpen(open);
987
1018
  if (open) setMeasured(false);
988
1019
  }
989
- const measure = (0, import_react7.useCallback)(() => {
1020
+ const measure = (0, import_react8.useCallback)(() => {
990
1021
  triggerRef.current?.measureInWindow((rawX, rawY, rawWidth, rawHeight) => {
991
1022
  const { x, y, width: triggerWidth, height: triggerHeight } = rotateRect(rawX, rawY, rawWidth, rawHeight, rotation, windowWidth, windowHeight);
992
1023
  const centerX = x + triggerWidth / 2;
@@ -1003,14 +1034,14 @@ function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
1003
1034
  setMeasured(true);
1004
1035
  });
1005
1036
  }, [contentWidth, contentHeight, windowWidth, windowHeight, rotation]);
1006
- (0, import_react7.useEffect)(() => {
1037
+ (0, import_react8.useEffect)(() => {
1007
1038
  if (open) measure();
1008
1039
  }, [open, measure]);
1009
1040
  return { align, maxHeight, measured, triggerRef, verticalAlign };
1010
1041
  }
1011
1042
 
1012
1043
  // src/SectionedDropdown.tsx
1013
- var import_jsx_runtime15 = require("react/jsx-runtime");
1044
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1014
1045
  var MENU_BORDER_WIDTH = 1;
1015
1046
  var TRIGGER_SIZE = 44;
1016
1047
  var MENU_MIN_WIDTH = 200;
@@ -1022,7 +1053,7 @@ var MENU_ITEM_HEIGHT_WITH_DESCRIPTION = 58;
1022
1053
  var MENU_DIVIDER_HEIGHT = 9;
1023
1054
  var MENU_VERTICAL_PADDING = 12;
1024
1055
  function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accentColor, mutedColor, onAccentColor, dark, align: forcedAlign, alignOverride, rotation: rotationOverride, autoDismiss = true, labelFontFamily = MONO_FONT, allClearLabels = { all: "All", clear: "Clear" } }) {
1025
- const ambientRotation = (0, import_core6.useRotation)();
1056
+ const ambientRotation = (0, import_core7.useRotation)();
1026
1057
  const rotation = rotationOverride ?? ambientRotation;
1027
1058
  const menuBg = dark ? "#000000" : "#FFFFFF";
1028
1059
  const menuBorderColor = (0, import_auto_paper9.getBlendedColor)(mutedColor, menuBg, 0.5);
@@ -1060,17 +1091,17 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
1060
1091
  }
1061
1092
  gaugeOffset += section.options.length;
1062
1093
  }
1063
- const anchorStyle = [styles12.anchor, open && styles12.anchorOpen];
1094
+ const anchorStyle = [styles13.anchor, open && styles13.anchorOpen];
1064
1095
  const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
1065
1096
  const dividerStyle = { backgroundColor: menuBorderColor };
1066
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native15.View, { style: anchorStyle, children: [
1067
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native15.View, { ref: triggerRef, collapsable: false, style: styles12.triggerBox, children: [
1068
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
1069
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_feedback_press4.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
1097
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native16.View, { style: anchorStyle, children: [
1098
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native16.View, { ref: triggerRef, collapsable: false, style: styles13.triggerBox, children: [
1099
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
1100
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_feedback_press4.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
1070
1101
  ] }),
1071
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native15.ScrollView, { style: [styles12.menu, menuStyle], contentContainerStyle: styles12.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native15.View, { children: [
1072
- sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native15.View, { style: [styles12.divider, dividerStyle] }),
1073
- section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1102
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native16.ScrollView, { style: [styles13.menu, menuStyle], contentContainerStyle: styles13.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native16.View, { children: [
1103
+ sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native16.View, { style: [styles13.divider, dividerStyle] }),
1104
+ section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1074
1105
  SingleSection,
1075
1106
  {
1076
1107
  section,
@@ -1083,7 +1114,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
1083
1114
  if (autoDismiss) host.close();
1084
1115
  }
1085
1116
  }
1086
- ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
1117
+ ) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
1087
1118
  ] }, section.id)) }) })
1088
1119
  ] });
1089
1120
  }
@@ -1095,18 +1126,18 @@ function SingleSection({
1095
1126
  labelFontFamily,
1096
1127
  onSelect
1097
1128
  }) {
1098
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: section.options.map((option) => {
1129
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: section.options.map((option) => {
1099
1130
  const selected = option.value === section.value;
1100
1131
  const taken = !selected && option.value === section.takenValue;
1101
1132
  const rowColor = selected ? onAccentColor : mutedColor;
1102
- const itemStyle = [styles12.item, selected && { backgroundColor: accentColor }, taken && styles12.itemTaken];
1133
+ const itemStyle = [styles13.item, selected && { backgroundColor: accentColor }, taken && styles13.itemTaken];
1103
1134
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
1104
1135
  const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
1105
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_feedback_press4.TouchableRipple, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native15.View, { style: styles12.itemRow, children: [
1106
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper9.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1107
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native15.View, { style: styles12.itemLabelColumn, children: [
1108
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper9.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
1109
- option.description && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper9.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
1136
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_feedback_press4.TouchableRipple, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native16.View, { style: styles13.itemRow, children: [
1137
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1138
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native16.View, { style: styles13.itemLabelColumn, children: [
1139
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
1140
+ option.description && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
1110
1141
  ] })
1111
1142
  ] }) }, option.value);
1112
1143
  }) });
@@ -1120,23 +1151,23 @@ function MultiSection({
1120
1151
  allClearLabels
1121
1152
  }) {
1122
1153
  const allSelected = section.value.length === section.options.length;
1123
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
1154
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
1124
1155
  section.options.map((option, i) => {
1125
1156
  const selected = section.value.includes(option.value);
1126
1157
  const rowColor = selected ? onAccentColor : mutedColor;
1127
1158
  const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
1128
1159
  const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
1129
- const itemStyle = [styles12.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
1160
+ const itemStyle = [styles13.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
1130
1161
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
1131
- return /* @__PURE__ */ (0, import_jsx_runtime15.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_runtime15.jsxs)(import_react_native15.View, { style: styles12.itemRow, children: [
1132
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper9.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1133
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper9.Text, { variant: "labelLarge", style: [styles12.itemLabelFlex, labelStyle], children: option.label })
1162
+ return /* @__PURE__ */ (0, import_jsx_runtime16.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_runtime16.jsxs)(import_react_native16.View, { style: styles13.itemRow, children: [
1163
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1164
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Text, { variant: "labelLarge", style: [styles13.itemLabelFlex, labelStyle], children: option.label })
1134
1165
  ] }) }, option.value);
1135
1166
  }),
1136
- section.allClear && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles12.item, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper9.Text, { variant: "labelLarge", style: [styles12.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
1167
+ section.allClear && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles13.item, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Text, { variant: "labelLarge", style: [styles13.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
1137
1168
  ] });
1138
1169
  }
1139
- var styles12 = import_react_native15.StyleSheet.create({
1170
+ var styles13 = import_react_native16.StyleSheet.create({
1140
1171
  allClearLabel: {
1141
1172
  fontWeight: "bold",
1142
1173
  textAlign: "center"
@@ -1153,7 +1184,7 @@ var styles12 = import_react_native15.StyleSheet.create({
1153
1184
  zIndex: 100
1154
1185
  },
1155
1186
  divider: {
1156
- height: import_react_native15.StyleSheet.hairlineWidth,
1187
+ height: import_react_native16.StyleSheet.hairlineWidth,
1157
1188
  marginVertical: 4
1158
1189
  },
1159
1190
  item: {
@@ -1206,24 +1237,24 @@ var styles12 = import_react_native15.StyleSheet.create({
1206
1237
  });
1207
1238
 
1208
1239
  // src/ControlSchemePicker.tsx
1209
- var import_jsx_runtime16 = require("react/jsx-runtime");
1240
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1210
1241
  function ControlSchemePicker({ id, host, value, onChange, options, takenValue, accentColor, mutedColor, onAccentColor, dark, align, alignOverride, rotation, labelFontFamily }) {
1211
- const isTouchPrimary = (0, import_core7.useIsTouchPrimaryDevice)();
1212
- if (import_react_native16.Platform.OS !== "web" || isTouchPrimary) return null;
1213
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SectionedDropdown, { id, host, icon: "keyboard-outline", accessibilityLabel: "Control scheme", sections: [{ kind: "single", id: "controls", options, value, onChange, takenValue }], accentColor, mutedColor, onAccentColor, dark, align, alignOverride, rotation, labelFontFamily });
1242
+ const isTouchPrimary = (0, import_core8.useIsTouchPrimaryDevice)();
1243
+ if (import_react_native17.Platform.OS !== "web" || isTouchPrimary) return null;
1244
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SectionedDropdown, { id, host, icon: "keyboard-outline", accessibilityLabel: "Control scheme", sections: [{ kind: "single", id: "controls", options, value, onChange, takenValue }], accentColor, mutedColor, onAccentColor, dark, align, alignOverride, rotation, labelFontFamily });
1214
1245
  }
1215
1246
 
1216
1247
  // src/CornerActionButtons.tsx
1217
1248
  var import_feedback_press5 = require("@rific/feedback-press");
1218
- var import_react_native17 = require("react-native");
1219
- var import_jsx_runtime17 = require("react/jsx-runtime");
1249
+ var import_react_native18 = require("react-native");
1250
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1220
1251
  function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon = "arrow-left", backLabel = "Back" }) {
1221
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
1222
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_feedback_press5.IconButton, { icon: backIcon, iconColor: fg, size: 24, style: [styles13.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: backLabel }),
1223
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_feedback_press5.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles13.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
1252
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
1253
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press5.IconButton, { icon: backIcon, iconColor: fg, size: 24, style: [styles14.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: backLabel }),
1254
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press5.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles14.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
1224
1255
  ] });
1225
1256
  }
1226
- var styles13 = import_react_native17.StyleSheet.create({
1257
+ var styles14 = import_react_native18.StyleSheet.create({
1227
1258
  back: {
1228
1259
  position: "absolute"
1229
1260
  },
@@ -1233,22 +1264,22 @@ var styles13 = import_react_native17.StyleSheet.create({
1233
1264
  });
1234
1265
 
1235
1266
  // src/CornerStatusBadges.tsx
1236
- var import_react_native18 = require("react-native");
1237
- var import_react_native_paper10 = require("react-native-paper");
1238
- var import_jsx_runtime18 = require("react/jsx-runtime");
1267
+ var import_react_native19 = require("react-native");
1268
+ var import_react_native_paper11 = require("react-native-paper");
1269
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1239
1270
  var BADGE_SIZE = 30;
1240
1271
  function StatusBadge({ icon }) {
1241
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native18.View, { style: styles14.badge, children: icon && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native_paper10.Icon, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
1272
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native19.View, { style: styles15.badge, children: icon && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Icon, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
1242
1273
  }
1243
1274
  function CornerStatusBadges({ orientationMode, p1OnRight, p1, p2 }) {
1244
1275
  const p1OnRightSide = orientationMode === "sideBySide" && p1OnRight;
1245
1276
  const p2OnLeftSide = orientationMode === "sideBySide" && !p1OnRight;
1246
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
1247
- p1 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native18.View, { pointerEvents: "none", style: p1OnRightSide ? styles14.bottomRight : styles14.bottomLeft, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StatusBadge, { icon: p1.icon }) }),
1248
- p2 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native18.View, { pointerEvents: "none", style: p2OnLeftSide ? styles14.topLeft : styles14.topRight, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StatusBadge, { icon: p2.icon }) })
1277
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
1278
+ p1 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native19.View, { pointerEvents: "none", style: p1OnRightSide ? styles15.bottomRight : styles15.bottomLeft, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StatusBadge, { icon: p1.icon }) }),
1279
+ p2 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native19.View, { pointerEvents: "none", style: p2OnLeftSide ? styles15.topLeft : styles15.topRight, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StatusBadge, { icon: p2.icon }) })
1249
1280
  ] });
1250
1281
  }
1251
- var styles14 = import_react_native18.StyleSheet.create({
1282
+ var styles15 = import_react_native19.StyleSheet.create({
1252
1283
  badge: {
1253
1284
  alignItems: "center",
1254
1285
  backgroundColor: "rgba(0,0,0,0.28)",
@@ -1269,10 +1300,10 @@ var styles14 = import_react_native18.StyleSheet.create({
1269
1300
  // src/InlineColorPicker.tsx
1270
1301
  var import_auto_paper10 = require("@rific/auto-paper");
1271
1302
  var import_feedback_press6 = require("@rific/feedback-press");
1272
- var import_core8 = require("@tastic/core");
1273
- var import_react_native19 = require("react-native");
1274
- var import_react_native_paper11 = require("react-native-paper");
1275
- var import_jsx_runtime19 = require("react/jsx-runtime");
1303
+ var import_core9 = require("@tastic/core");
1304
+ var import_react_native20 = require("react-native");
1305
+ var import_react_native_paper12 = require("react-native-paper");
1306
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1276
1307
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
1277
1308
  var DEFAULT_SIZE = 48;
1278
1309
  var SWATCH_SIZE = 28;
@@ -1286,7 +1317,7 @@ function widthForColumns(columns) {
1286
1317
  return SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * columns + SWATCHES_GAP * (columns - 1);
1287
1318
  }
1288
1319
  function getInlineColorPickerContentSize(swatchCount, windowWidth, columns) {
1289
- const autoColumns = (0, import_core8.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
1320
+ const autoColumns = (0, import_core9.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
1290
1321
  const resolvedColumns = columns ?? autoColumns;
1291
1322
  const width = widthForColumns(resolvedColumns);
1292
1323
  const rows = Math.ceil(swatchCount / resolvedColumns);
@@ -1294,10 +1325,10 @@ function getInlineColorPickerContentSize(swatchCount, windowWidth, columns) {
1294
1325
  return { width, height };
1295
1326
  }
1296
1327
  function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = import_auto_paper10.defaultColors, takenValue, allowSwapTaken, dark, align: forcedAlign, alignOverride, rotation: rotationOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
1297
- const ambientRotation = (0, import_core8.useRotation)();
1328
+ const ambientRotation = (0, import_core9.useRotation)();
1298
1329
  const rotation = rotationOverride ?? ambientRotation;
1299
1330
  const menuBg = dark ? "#000000" : "#FFFFFF";
1300
- const { width: windowWidth } = (0, import_react_native19.useWindowDimensions)();
1331
+ const { width: windowWidth } = (0, import_react_native20.useWindowDimensions)();
1301
1332
  const { width: swatchesWidth, height: swatchesHeight } = getInlineColorPickerContentSize(swatches.length, windowWidth, columns);
1302
1333
  const open = host.openId === id;
1303
1334
  const auto = useAutoAlign(open, swatchesWidth, swatchesHeight, rotation);
@@ -1308,14 +1339,14 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1308
1339
  const displayValue = displayFor(value);
1309
1340
  const contrastColor = (0, import_auto_paper10.getContrastColor)(displayValue);
1310
1341
  const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
1311
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native19.View, { style: [styles15.anchor, open && styles15.anchorOpen], children: [
1312
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_feedback_press6.TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles15.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native19.View, { ref: triggerRef, collapsable: false, style: styles15.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Text, { style: [styles15.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
1313
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native19.ScrollView, { style: [styles15.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles15.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
1342
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native20.View, { style: [styles16.anchor, open && styles16.anchorOpen], children: [
1343
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_feedback_press6.TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles16.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native20.View, { ref: triggerRef, collapsable: false, style: styles16.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Text, { style: [styles16.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
1344
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native20.ScrollView, { style: [styles16.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles16.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
1314
1345
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
1315
1346
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
1316
1347
  const swappable = taken && allowSwapTaken;
1317
1348
  const swatchDisplay = displayFor(swatch.value);
1318
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1349
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1319
1350
  import_feedback_press6.TouchableRipple,
1320
1351
  {
1321
1352
  disabled: taken && !swappable,
@@ -1324,15 +1355,15 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1324
1355
  if (autoDismiss) host.close();
1325
1356
  },
1326
1357
  borderless: true,
1327
- style: [styles15.swatch, { backgroundColor: swatchDisplay }, selected && styles15.swatchSelected, taken && !swappable && styles15.swatchTaken],
1328
- children: selected ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Icon, { source: "check", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Icon, { source: "close", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native19.View, {})
1358
+ style: [styles16.swatch, { backgroundColor: swatchDisplay }, selected && styles16.swatchSelected, taken && !swappable && styles16.swatchTaken],
1359
+ children: selected ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Icon, { source: "check", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Icon, { source: "close", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native20.View, {})
1329
1360
  },
1330
1361
  swatch.value
1331
1362
  );
1332
1363
  }) }) })
1333
1364
  ] });
1334
1365
  }
1335
- var styles15 = import_react_native19.StyleSheet.create({
1366
+ var styles16 = import_react_native20.StyleSheet.create({
1336
1367
  anchor: {
1337
1368
  position: "relative"
1338
1369
  },
@@ -1406,9 +1437,9 @@ var styles15 = import_react_native19.StyleSheet.create({
1406
1437
  // src/LabeledDropdown.tsx
1407
1438
  var import_auto_paper11 = require("@rific/auto-paper");
1408
1439
  var import_feedback_press7 = require("@rific/feedback-press");
1409
- var import_react_native20 = require("react-native");
1410
- var import_react_native_paper12 = require("react-native-paper");
1411
- var import_jsx_runtime20 = require("react/jsx-runtime");
1440
+ var import_react_native21 = require("react-native");
1441
+ var import_react_native_paper13 = require("react-native-paper");
1442
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1412
1443
  var TRIGGER_HEIGHT = 28;
1413
1444
  var POPOVER_WIDTH = 180;
1414
1445
  var ROW_HEIGHT = 36;
@@ -1431,22 +1462,22 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
1431
1462
  onChange(option.value);
1432
1463
  host.close();
1433
1464
  };
1434
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native20.View, { style: [styles16.anchor, open && styles16.anchorOpen], children: [
1435
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => host.toggle(id), style: styles16.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native20.View, { ref: triggerRef, collapsable: false, style: styles16.triggerInner, children: [
1436
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Text, { style: [styles16.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
1437
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Icon, { source: "menu-down", size: 16, color })
1465
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react_native21.View, { style: [styles17.anchor, open && styles17.anchorOpen], children: [
1466
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => host.toggle(id), style: styles17.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react_native21.View, { ref: triggerRef, collapsable: false, style: styles17.triggerInner, children: [
1467
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native_paper13.Text, { style: [styles17.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
1468
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native_paper13.Icon, { source: "menu-down", size: 16, color })
1438
1469
  ] }) }),
1439
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native20.ScrollView, { style: [styles16.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles16.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
1470
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native21.ScrollView, { style: [styles17.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles17.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
1440
1471
  const isSelected = option.value === value;
1441
1472
  const onColor = (0, import_auto_paper11.getContrastColor)(color);
1442
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => handleSelect(option), style: [styles16.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native20.View, { style: styles16.rowInner, children: [
1443
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
1444
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Text, { style: [styles16.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
1473
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => handleSelect(option), style: [styles17.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react_native21.View, { style: styles17.rowInner, children: [
1474
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native_paper13.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
1475
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native_paper13.Text, { style: [styles17.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
1445
1476
  ] }) }, option.value);
1446
1477
  }) }) })
1447
1478
  ] });
1448
1479
  }
1449
- var styles16 = import_react_native20.StyleSheet.create({
1480
+ var styles17 = import_react_native21.StyleSheet.create({
1450
1481
  anchor: {
1451
1482
  position: "relative"
1452
1483
  },
@@ -1505,21 +1536,21 @@ function loadSkiaWeb() {
1505
1536
 
1506
1537
  // src/PlayerSetupPanel.tsx
1507
1538
  var import_profile2 = require("@tastic/profile");
1508
- var import_react_native22 = require("react-native");
1509
- var import_react_native_paper14 = require("react-native-paper");
1539
+ var import_react_native23 = require("react-native");
1540
+ var import_react_native_paper15 = require("react-native-paper");
1510
1541
 
1511
1542
  // src/ReadyButton.tsx
1512
1543
  var import_feedback_press8 = require("@rific/feedback-press");
1513
- var import_react_native21 = require("react-native");
1514
- var import_react_native_paper13 = require("react-native-paper");
1515
- var import_jsx_runtime21 = require("react/jsx-runtime");
1544
+ var import_react_native22 = require("react-native");
1545
+ var import_react_native_paper14 = require("react-native-paper");
1546
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1516
1547
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
1517
- const readyButtonStyle = [styles17.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
1548
+ const readyButtonStyle = [styles18.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
1518
1549
  const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
1519
1550
  const labelColorStyle = { color: ready ? "#000000" : color };
1520
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_feedback_press8.TouchableRipple, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native_paper13.Text, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
1551
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_feedback_press8.TouchableRipple, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native_paper14.Text, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
1521
1552
  }
1522
- var styles17 = import_react_native21.StyleSheet.create({
1553
+ var styles18 = import_react_native22.StyleSheet.create({
1523
1554
  readyButton: {
1524
1555
  alignItems: "center",
1525
1556
  borderRadius: 12,
@@ -1532,18 +1563,18 @@ var styles17 = import_react_native21.StyleSheet.create({
1532
1563
  });
1533
1564
 
1534
1565
  // src/usePopoverHost.ts
1535
- var import_react8 = require("react");
1566
+ var import_react9 = require("react");
1536
1567
  function usePopoverHost() {
1537
- const [openId, setOpenId] = (0, import_react8.useState)(null);
1538
- const toggle = (0, import_react8.useCallback)((id) => {
1568
+ const [openId, setOpenId] = (0, import_react9.useState)(null);
1569
+ const toggle = (0, import_react9.useCallback)((id) => {
1539
1570
  setOpenId((prev) => prev === id ? null : id);
1540
1571
  }, []);
1541
- const close = (0, import_react8.useCallback)(() => setOpenId(null), []);
1572
+ const close = (0, import_react9.useCallback)(() => setOpenId(null), []);
1542
1573
  return { openId, toggle, close };
1543
1574
  }
1544
1575
 
1545
1576
  // src/PlayerSetupPanel.tsx
1546
- var import_jsx_runtime22 = require("react/jsx-runtime");
1577
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1547
1578
  function getColorPopoverId(idPrefix) {
1548
1579
  return `${idPrefix}-color`;
1549
1580
  }
@@ -1558,16 +1589,16 @@ function PlayerSetupPanel({ idPrefix, host, label, labelFontFamily, color, onCol
1558
1589
  const showProfilePicker = isHuman && profiles !== void 0 && !!onProfileSelect;
1559
1590
  const showCpuDifficulty = !isHuman && cpuDifficulty !== void 0 && cpuDifficultyOptions !== void 0 && !!onCpuDifficultyChange;
1560
1591
  const selectedProfile = profiles?.find((p) => p.id === selectedProfileId) ?? null;
1561
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react_native22.View, { style: [styles18.panel, ownPopoverOpen && styles18.panelOpen], children: [
1562
- showProfilePicker ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_profile2.ProfilePicker, { idPrefix, host: popover, profiles, selectedId: selectedProfileId ?? null, takenId: takenProfileId, color, dark, guestLabel: guestLabel ?? "GUEST", onSelect: (profile) => onProfileSelect?.(profile), onManage: onManageProfiles }) : showCpuDifficulty ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(LabeledDropdown, { id: getCpuDifficultyPopoverId(idPrefix), host: popover, options: cpuDifficultyOptions, value: cpuDifficulty, onChange: onCpuDifficultyChange, color, dark, alignOverride: cpuDifficultyAlignOverride }) : label && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native_paper14.Text, { variant: "labelSmall", style: { color, fontFamily: labelFontFamily }, children: label }),
1563
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react_native22.View, { style: [styles18.pickerRow, pickerRowPopoverOpen && styles18.pickerRowOpen], children: [
1564
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(InlineColorPicker, { id: getColorPopoverId(idPrefix), host: popover, value: color, onChange: onColorChange, swatches, takenValue: takenColor, allowSwapTaken, alignOverride: colorAlignOverride, rotation: colorRotation, dark, tag: isHuman ? selectedProfile?.tag : void 0, icon: isHuman ? humanIcon : cpuIcon }),
1592
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_native23.View, { style: [styles19.panel, ownPopoverOpen && styles19.panelOpen], children: [
1593
+ showProfilePicker ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_profile2.ProfilePicker, { idPrefix, host: popover, profiles, selectedId: selectedProfileId ?? null, takenId: takenProfileId, color, dark, guestLabel: guestLabel ?? "GUEST", onSelect: (profile) => onProfileSelect?.(profile), onManage: onManageProfiles }) : showCpuDifficulty ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(LabeledDropdown, { id: getCpuDifficultyPopoverId(idPrefix), host: popover, options: cpuDifficultyOptions, value: cpuDifficulty, onChange: onCpuDifficultyChange, color, dark, alignOverride: cpuDifficultyAlignOverride }) : label && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native_paper15.Text, { variant: "labelSmall", style: { color, fontFamily: labelFontFamily }, children: label }),
1594
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_native23.View, { style: [styles19.pickerRow, pickerRowPopoverOpen && styles19.pickerRowOpen], children: [
1595
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(InlineColorPicker, { id: getColorPopoverId(idPrefix), host: popover, value: color, onChange: onColorChange, swatches, takenValue: takenColor, allowSwapTaken, alignOverride: colorAlignOverride, rotation: colorRotation, dark, tag: isHuman ? selectedProfile?.tag : void 0, icon: isHuman ? humanIcon : cpuIcon }),
1565
1596
  secondPicker
1566
1597
  ] }),
1567
- isHuman && showReadyButton && onToggleReady && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReadyButton, { color, ready: ready ?? false, onToggleReady })
1598
+ isHuman && showReadyButton && onToggleReady && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ReadyButton, { color, ready: ready ?? false, onToggleReady })
1568
1599
  ] });
1569
1600
  }
1570
- var styles18 = import_react_native22.StyleSheet.create({
1601
+ var styles19 = import_react_native23.StyleSheet.create({
1571
1602
  panel: {
1572
1603
  alignItems: "center",
1573
1604
  gap: 12
@@ -1599,29 +1630,29 @@ var styles18 = import_react_native22.StyleSheet.create({
1599
1630
  });
1600
1631
 
1601
1632
  // src/PressAwayOverlay.tsx
1602
- var import_react_native23 = require("react-native");
1603
- var import_jsx_runtime23 = require("react/jsx-runtime");
1633
+ var import_react_native24 = require("react-native");
1634
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1604
1635
  function PressAwayOverlay({ active, onPress, style }) {
1605
1636
  if (!active) return null;
1606
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native23.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native23.StyleSheet.absoluteFill, style] });
1637
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react_native24.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native24.StyleSheet.absoluteFill, style] });
1607
1638
  }
1608
1639
 
1609
1640
  // src/SharedActionBand.tsx
1610
1641
  var import_feedback_press9 = require("@rific/feedback-press");
1611
- var import_react_native24 = require("react-native");
1612
- var import_jsx_runtime24 = require("react/jsx-runtime");
1642
+ var import_react_native25 = require("react-native");
1643
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1613
1644
  function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
1614
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react_native24.View, { style: [styles19.column, popoverOpen && styles19.columnOpen], children: [
1615
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react_native24.View, { style: styles19.actionsRow, children: [
1616
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_feedback_press9.IconButton, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1617
- onRandomize && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_feedback_press9.IconButton, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1618
- onReset && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_feedback_press9.IconButton, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
1619
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_feedback_press9.IconButton, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
1645
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_native25.View, { style: [styles20.column, popoverOpen && styles20.columnOpen], children: [
1646
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_native25.View, { style: styles20.actionsRow, children: [
1647
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_feedback_press9.IconButton, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1648
+ onRandomize && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_feedback_press9.IconButton, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1649
+ onReset && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_feedback_press9.IconButton, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
1650
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_feedback_press9.IconButton, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
1620
1651
  ] }),
1621
1652
  children
1622
1653
  ] });
1623
1654
  }
1624
- var styles19 = import_react_native24.StyleSheet.create({
1655
+ var styles20 = import_react_native25.StyleSheet.create({
1625
1656
  actionsRow: {
1626
1657
  alignItems: "center",
1627
1658
  flexDirection: "row",
@@ -1637,10 +1668,10 @@ var styles19 = import_react_native24.StyleSheet.create({
1637
1668
  });
1638
1669
 
1639
1670
  // src/StaggeredWord.tsx
1640
- var import_react9 = require("react");
1641
- var import_react_native25 = require("react-native");
1671
+ var import_react10 = require("react");
1672
+ var import_react_native26 = require("react-native");
1642
1673
  var import_react_native_reanimated2 = __toESM(require("react-native-reanimated"));
1643
- var import_jsx_runtime25 = require("react/jsx-runtime");
1674
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1644
1675
  var STAGGER_MS = 45;
1645
1676
  var LETTER_DURATION_MS = 320;
1646
1677
  function getStaggeredWordDuration(letterCount) {
@@ -1648,7 +1679,7 @@ function getStaggeredWordDuration(letterCount) {
1648
1679
  }
1649
1680
  function AnimatedLetter({ char, index, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }) {
1650
1681
  const progress = (0, import_react_native_reanimated2.useSharedValue)(reducedMotion ? 1 : 0);
1651
- (0, import_react9.useEffect)(() => {
1682
+ (0, import_react10.useEffect)(() => {
1652
1683
  if (reducedMotion) return;
1653
1684
  progress.value = (0, import_react_native_reanimated2.withDelay)(index * STAGGER_MS, (0, import_react_native_reanimated2.withTiming)(1, { duration: LETTER_DURATION_MS, easing: import_react_native_reanimated2.Easing.out(import_react_native_reanimated2.Easing.back(1.5)) }));
1654
1685
  }, [reducedMotion]);
@@ -1656,12 +1687,12 @@ function AnimatedLetter({ char, index, color, reducedMotion, fontFamily, fontSiz
1656
1687
  opacity: progress.value,
1657
1688
  transform: [{ translateY: (1 - progress.value) * 12 }, { scale: 0.85 + progress.value * 0.15 }]
1658
1689
  }));
1659
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native_reanimated2.default.Text, { allowFontScaling: false, style: [{ color, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, style], children: char });
1690
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native_reanimated2.default.Text, { allowFontScaling: false, style: [{ color, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, style], children: char });
1660
1691
  }
1661
1692
  function StaggeredWord({ word, color, fontFamily, fontSize, lineHeight, reducedMotion, startIndex = 0, fontWeight = "bold", letterSpacing, style, onLayout }) {
1662
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native25.View, { style: [styles20.row, style], onLayout, children: word.split("").map((char, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(AnimatedLetter, { char, index: startIndex + i, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, i)) });
1693
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native26.View, { style: [styles21.row, style], onLayout, children: word.split("").map((char, i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AnimatedLetter, { char, index: startIndex + i, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, i)) });
1663
1694
  }
1664
- var styles20 = import_react_native25.StyleSheet.create({
1695
+ var styles21 = import_react_native26.StyleSheet.create({
1665
1696
  row: {
1666
1697
  flexDirection: "row"
1667
1698
  }
@@ -1669,53 +1700,53 @@ var styles20 = import_react_native25.StyleSheet.create({
1669
1700
 
1670
1701
  // src/UpdateDialog.tsx
1671
1702
  var import_updater2 = require("@rific/updater");
1672
- var import_jsx_runtime26 = require("react/jsx-runtime");
1703
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1673
1704
  function UpdateDialog({ rotation = 0, autoCheck = true, autoPrompt = true, onError }) {
1674
1705
  const { manifest, onConfirm, respond } = useUpdateConfirmBridge();
1675
1706
  (0, import_updater2.useUpdater)({ autoCheck, autoPrompt, onConfirm, onError });
1676
1707
  const date = manifest ? new Date(manifest.createdAt) : null;
1677
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ConfirmDialog, { visible: !!manifest, title: "Update Available", message: date ? `Released ${date.toLocaleDateString()} at ${date.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation, onConfirm: () => respond(true), onCancel: () => respond(false) });
1708
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ConfirmDialog, { visible: !!manifest, title: "Update Available", message: date ? `Released ${date.toLocaleDateString()} at ${date.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation, onConfirm: () => respond(true), onCancel: () => respond(false) });
1678
1709
  }
1679
1710
 
1680
1711
  // src/useQuitConfirmation.ts
1681
- var import_react10 = require("react");
1712
+ var import_react11 = require("react");
1682
1713
  function useQuitConfirmation(hasProgress, onConfirmedBack) {
1683
- const [confirmVisible, setConfirmVisible] = (0, import_react10.useState)(false);
1684
- const hasProgressRef = (0, import_react10.useRef)(hasProgress);
1685
- (0, import_react10.useEffect)(() => {
1714
+ const [confirmVisible, setConfirmVisible] = (0, import_react11.useState)(false);
1715
+ const hasProgressRef = (0, import_react11.useRef)(hasProgress);
1716
+ (0, import_react11.useEffect)(() => {
1686
1717
  hasProgressRef.current = hasProgress;
1687
1718
  });
1688
- const onConfirmedBackRef = (0, import_react10.useRef)(onConfirmedBack);
1689
- (0, import_react10.useEffect)(() => {
1719
+ const onConfirmedBackRef = (0, import_react11.useRef)(onConfirmedBack);
1720
+ (0, import_react11.useEffect)(() => {
1690
1721
  onConfirmedBackRef.current = onConfirmedBack;
1691
1722
  });
1692
- const requestBack = (0, import_react10.useCallback)(() => {
1723
+ const requestBack = (0, import_react11.useCallback)(() => {
1693
1724
  if (hasProgressRef.current()) setConfirmVisible(true);
1694
1725
  else onConfirmedBackRef.current();
1695
1726
  }, []);
1696
- const cancelBack = (0, import_react10.useCallback)(() => setConfirmVisible(false), []);
1727
+ const cancelBack = (0, import_react11.useCallback)(() => setConfirmVisible(false), []);
1697
1728
  return { confirmVisible, requestBack, cancelBack };
1698
1729
  }
1699
1730
 
1700
1731
  // src/useZoneClampedAlign.ts
1701
1732
  var import_split_screen = require("@tastic/split-screen");
1702
- var import_react11 = require("react");
1703
- var import_react_native26 = require("react-native");
1704
- var import_react_native_safe_area_context = require("react-native-safe-area-context");
1733
+ var import_react12 = require("react");
1734
+ var import_react_native27 = require("react-native");
1735
+ var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
1705
1736
  var EDGE_MARGIN2 = 12;
1706
1737
  function useZoneClampedAlign(open, contentWidth, contentHeight, rotation = 0) {
1707
1738
  const auto = useAutoAlign(open, contentWidth, contentHeight, rotation);
1708
1739
  const zone = (0, import_split_screen.useZoneBounds)();
1709
- const { height: windowHeight } = (0, import_react_native26.useWindowDimensions)();
1710
- const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
1740
+ const { height: windowHeight } = (0, import_react_native27.useWindowDimensions)();
1741
+ const insets = (0, import_react_native_safe_area_context2.useSafeAreaInsets)();
1711
1742
  const shouldMeasure = open && auto.measured && zone !== null;
1712
- const [ownBounds, setOwnBounds] = (0, import_react11.useState)(null);
1713
- const [prevShouldMeasure, setPrevShouldMeasure] = (0, import_react11.useState)(shouldMeasure);
1743
+ const [ownBounds, setOwnBounds] = (0, import_react12.useState)(null);
1744
+ const [prevShouldMeasure, setPrevShouldMeasure] = (0, import_react12.useState)(shouldMeasure);
1714
1745
  if (shouldMeasure !== prevShouldMeasure) {
1715
1746
  setPrevShouldMeasure(shouldMeasure);
1716
1747
  setOwnBounds(null);
1717
1748
  }
1718
- (0, import_react11.useEffect)(() => {
1749
+ (0, import_react12.useEffect)(() => {
1719
1750
  if (!shouldMeasure) return;
1720
1751
  auto.triggerRef.current?.measureInWindow((_x, y, _width, height) => {
1721
1752
  const belowShared = zone.zoneSide === "belowShared";
@@ -1757,6 +1788,7 @@ function useZoneClampedAlign(open, contentWidth, contentHeight, rotation = 0) {
1757
1788
  PopoverBody,
1758
1789
  PressAwayOverlay,
1759
1790
  ReadyButton,
1791
+ RotatedFrame,
1760
1792
  SectionedDropdown,
1761
1793
  SharedActionBand,
1762
1794
  SkiaGate,