@tastic/hud 0.3.0 → 0.5.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.mjs CHANGED
@@ -1,17 +1,85 @@
1
+ // src/AchievementRow.tsx
2
+ import { useAutoPaperTheme } from "@rific/auto-paper";
3
+ import { StyleSheet, View } from "react-native";
4
+ import { Icon, ProgressBar, Text } from "react-native-paper";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ var LOCKED_BADGE_COLOR = "rgba(128,128,128,0.3)";
7
+ function AchievementRow({ icon, title, description, badgeColor, unlockedLabel, checkColor, progress, deviceMarker, fg: fgOverride, fgMuted: fgMutedOverride, sectionBg: sectionBgOverride }) {
8
+ const { dark } = useAutoPaperTheme();
9
+ const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
10
+ const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
11
+ const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
12
+ const unlocked = unlockedLabel !== void 0;
13
+ return /* @__PURE__ */ jsxs(View, { style: [styles.row, { backgroundColor: sectionBg }], children: [
14
+ /* @__PURE__ */ jsx(View, { style: [styles.badge, { backgroundColor: badgeColor }], children: /* @__PURE__ */ jsx(Icon, { source: icon, size: 20, color: unlocked ? "#000000" : fgMuted }) }),
15
+ /* @__PURE__ */ jsxs(View, { style: styles.text, children: [
16
+ /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: [styles.boldText, { color: fg }], children: title }),
17
+ /* @__PURE__ */ jsx(Text, { variant: "bodySmall", style: { color: fgMuted }, children: description }),
18
+ deviceMarker && /* @__PURE__ */ jsxs(View, { style: styles.deviceMarker, children: [
19
+ /* @__PURE__ */ jsx(Icon, { source: "earth", size: 11, color: fgMuted }),
20
+ /* @__PURE__ */ jsx(Text, { variant: "labelSmall", style: { color: fgMuted }, children: "Same for every profile" })
21
+ ] }),
22
+ !unlocked && progress !== void 0 && /* @__PURE__ */ jsx(ProgressBar, { progress, color: checkColor ?? badgeColor, style: styles.progressBar })
23
+ ] }),
24
+ unlocked ? /* @__PURE__ */ jsxs(View, { style: styles.status, children: [
25
+ /* @__PURE__ */ jsx(Icon, { source: "check-circle", size: 20, color: checkColor ?? badgeColor }),
26
+ /* @__PURE__ */ jsx(Text, { variant: "labelSmall", style: { color: fgMuted }, children: unlockedLabel })
27
+ ] }) : /* @__PURE__ */ jsx(Icon, { source: "lock-outline", size: 20, color: fgMuted })
28
+ ] });
29
+ }
30
+ var styles = StyleSheet.create({
31
+ badge: {
32
+ alignItems: "center",
33
+ borderRadius: 10,
34
+ height: 36,
35
+ justifyContent: "center",
36
+ width: 36
37
+ },
38
+ boldText: {
39
+ fontWeight: "bold"
40
+ },
41
+ deviceMarker: {
42
+ alignItems: "center",
43
+ flexDirection: "row",
44
+ gap: 4
45
+ },
46
+ progressBar: {
47
+ borderRadius: 4,
48
+ height: 6,
49
+ marginTop: 6
50
+ },
51
+ row: {
52
+ alignItems: "center",
53
+ borderRadius: 12,
54
+ flexDirection: "row",
55
+ gap: 12,
56
+ padding: 12
57
+ },
58
+ status: {
59
+ alignItems: "center",
60
+ gap: 2,
61
+ width: 64
62
+ },
63
+ text: {
64
+ flex: 1,
65
+ gap: 2
66
+ }
67
+ });
68
+
1
69
  // src/BaseSettingsDialog.tsx
2
- import { AutoAppearancePicker, Dialog, useAutoPaperTheme } from "@rific/auto-paper";
70
+ import { AutoAppearancePicker, Dialog, useAutoPaperTheme as useAutoPaperTheme2 } from "@rific/auto-paper";
3
71
  import { Button, SoundContext, TouchableRipple, useHapticSettings, useSoundSettings, useVibration } from "@rific/feedback-press";
4
72
  import { useUpdater } from "@rific/updater";
5
73
  import { useIsTouchPrimaryDevice } from "@tastic/core";
6
74
  import { useContext, useState } from "react";
7
- import { Platform, ScrollView, StyleSheet, View } from "react-native";
8
- import { Icon, Portal, Text } from "react-native-paper";
9
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
75
+ import { Platform, ScrollView, StyleSheet as StyleSheet2, View as View2 } from "react-native";
76
+ import { Icon as Icon2, Portal, Text as Text2 } from "react-native-paper";
77
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
10
78
  function SettingIcon({ source, color, containerColor }) {
11
- return /* @__PURE__ */ jsx(View, { style: [styles.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ jsx(Icon, { source, size: 18, color }) });
79
+ return /* @__PURE__ */ jsx2(View2, { style: [styles2.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ jsx2(Icon2, { source, size: 18, color }) });
12
80
  }
13
81
  function BaseSettingsDialog({ visible, onDismiss, rotation = 0, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, children }) {
14
- const { dark, colors } = useAutoPaperTheme();
82
+ const { dark, colors } = useAutoPaperTheme2();
15
83
  const isTouchPrimary = useIsTouchPrimaryDevice();
16
84
  const { settings: hapticSettings, set: setHapticSettings } = useHapticSettings();
17
85
  const { settings: soundSettings, set: setSoundSettings } = useSoundSettings();
@@ -33,27 +101,27 @@ function BaseSettingsDialog({ visible, onDismiss, rotation = 0, version, lockOri
33
101
  const fg = dark ? "#FFFFFF" : "#000000";
34
102
  const cardBg = dark ? "#111111" : "#F2F2F2";
35
103
  const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
36
- return /* @__PURE__ */ jsxs(Fragment, { children: [
37
- /* @__PURE__ */ jsxs(Dialog, { visible, onDismiss, style: [styles.dialog, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
38
- /* @__PURE__ */ jsx(Dialog.Title, { children: "Settings" }),
39
- /* @__PURE__ */ jsx(Dialog.ScrollArea, { style: styles.scrollArea, children: /* @__PURE__ */ jsxs(ScrollView, { contentContainerStyle: styles.content, showsVerticalScrollIndicator: false, children: [
40
- /* @__PURE__ */ jsxs(View, { style: styles.toggleGroup, children: [
41
- showLockOrientation && /* @__PURE__ */ jsx(TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ jsxs(View, { style: styles.toggleContent, children: [
42
- /* @__PURE__ */ jsx(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
43
- /* @__PURE__ */ jsxs(View, { style: styles.flexShrink, children: [
44
- /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
45
- /* @__PURE__ */ jsx(Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
104
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
105
+ /* @__PURE__ */ jsxs2(Dialog, { visible, onDismiss, style: [styles2.dialog, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
106
+ /* @__PURE__ */ jsx2(Dialog.Title, { children: "Settings" }),
107
+ /* @__PURE__ */ jsx2(Dialog.ScrollArea, { style: styles2.scrollArea, children: /* @__PURE__ */ jsxs2(ScrollView, { contentContainerStyle: styles2.content, showsVerticalScrollIndicator: false, children: [
108
+ /* @__PURE__ */ jsxs2(View2, { style: styles2.toggleGroup, children: [
109
+ showLockOrientation && /* @__PURE__ */ jsx2(TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles2.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ jsxs2(View2, { style: styles2.toggleContent, children: [
110
+ /* @__PURE__ */ jsx2(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
111
+ /* @__PURE__ */ jsxs2(View2, { style: styles2.flexShrink, children: [
112
+ /* @__PURE__ */ jsx2(Text2, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
113
+ /* @__PURE__ */ jsx2(Text2, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
46
114
  ] })
47
115
  ] }) }),
48
- showEdgeGuard && /* @__PURE__ */ jsx(TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ jsxs(View, { style: styles.toggleContent, children: [
49
- /* @__PURE__ */ jsx(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
50
- /* @__PURE__ */ jsxs(View, { style: styles.flexShrink, children: [
51
- /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
52
- /* @__PURE__ */ jsx(Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
116
+ showEdgeGuard && /* @__PURE__ */ jsx2(TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles2.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ jsxs2(View2, { style: styles2.toggleContent, children: [
117
+ /* @__PURE__ */ jsx2(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
118
+ /* @__PURE__ */ jsxs2(View2, { style: styles2.flexShrink, children: [
119
+ /* @__PURE__ */ jsx2(Text2, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
120
+ /* @__PURE__ */ jsx2(Text2, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
53
121
  ] })
54
122
  ] }) }),
55
- (showSound || showHaptics) && /* @__PURE__ */ jsxs(View, { style: styles.toggleRow, children: [
56
- showSound && /* @__PURE__ */ jsx(
123
+ (showSound || showHaptics) && /* @__PURE__ */ jsxs2(View2, { style: styles2.toggleRow, children: [
124
+ showSound && /* @__PURE__ */ jsx2(
57
125
  TouchableRipple,
58
126
  {
59
127
  soundDisabled: true,
@@ -62,15 +130,15 @@ function BaseSettingsDialog({ visible, onDismiss, rotation = 0, version, lockOri
62
130
  setSoundSettings({ enabled });
63
131
  if (enabled) sound.selection?.();
64
132
  },
65
- style: styles.toggleButtonInRow,
133
+ style: styles2.toggleButtonInRow,
66
134
  accessibilityLabel: `Sound ${soundSettings.enabled ? "on" : "off"}`,
67
- children: /* @__PURE__ */ jsxs(View, { style: styles.toggleContent, children: [
68
- /* @__PURE__ */ jsx(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
69
- /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
135
+ children: /* @__PURE__ */ jsxs2(View2, { style: styles2.toggleContent, children: [
136
+ /* @__PURE__ */ jsx2(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
137
+ /* @__PURE__ */ jsx2(Text2, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
70
138
  ] })
71
139
  }
72
140
  ),
73
- showHaptics && /* @__PURE__ */ jsx(
141
+ showHaptics && /* @__PURE__ */ jsx2(
74
142
  TouchableRipple,
75
143
  {
76
144
  hapticDisabled: true,
@@ -79,40 +147,40 @@ function BaseSettingsDialog({ visible, onDismiss, rotation = 0, version, lockOri
79
147
  setHapticSettings({ vibrate });
80
148
  if (vibrate) forceHapticFeedback();
81
149
  },
82
- style: styles.toggleButtonInRow,
150
+ style: styles2.toggleButtonInRow,
83
151
  accessibilityLabel: `Haptics ${hapticSettings.vibrate ? "on" : "off"}`,
84
- children: /* @__PURE__ */ jsxs(View, { style: styles.toggleContent, children: [
85
- /* @__PURE__ */ jsx(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
86
- /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
152
+ children: /* @__PURE__ */ jsxs2(View2, { style: styles2.toggleContent, children: [
153
+ /* @__PURE__ */ jsx2(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
154
+ /* @__PURE__ */ jsx2(Text2, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
87
155
  ] })
88
156
  }
89
157
  )
90
158
  ] })
91
159
  ] }),
92
- showAppearance && /* @__PURE__ */ jsxs(View, { style: styles.section, children: [
93
- /* @__PURE__ */ jsx(Text, { variant: "labelMedium", style: [styles.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
94
- /* @__PURE__ */ jsx(AutoAppearancePicker, { showLabels: false })
160
+ showAppearance && /* @__PURE__ */ jsxs2(View2, { style: styles2.section, children: [
161
+ /* @__PURE__ */ jsx2(Text2, { variant: "labelMedium", style: [styles2.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
162
+ /* @__PURE__ */ jsx2(AutoAppearancePicker, { showLabels: false })
95
163
  ] }),
96
164
  children,
97
- showUpdateCheck && /* @__PURE__ */ jsxs(View, { style: styles.section, children: [
98
- /* @__PURE__ */ jsxs(Text, { variant: "labelSmall", style: [styles.sectionLabel, { color: colors.onSurfaceVariant }], children: [
165
+ showUpdateCheck && /* @__PURE__ */ jsxs2(View2, { style: styles2.section, children: [
166
+ /* @__PURE__ */ jsxs2(Text2, { variant: "labelSmall", style: [styles2.sectionLabel, { color: colors.onSurfaceVariant }], children: [
99
167
  "VERSION ",
100
168
  version,
101
169
  updateReady ? " \xB7 UPDATE READY" : ""
102
170
  ] }),
103
- /* @__PURE__ */ jsx(Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
171
+ /* @__PURE__ */ jsx2(Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
104
172
  ] })
105
173
  ] }) })
106
174
  ] }),
107
- infoMessage && /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(View, { style: styles.overlay, children: /* @__PURE__ */ jsxs(View, { style: [styles.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
108
- /* @__PURE__ */ jsx(Icon, { source: "information-outline", size: 64, color: colors.secondary }),
109
- /* @__PURE__ */ jsx(Text, { variant: "headlineLarge", style: [styles.overlayTitle, { color: colors.secondary }], children: infoMessage.title }),
110
- /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: [styles.overlayBody, { color: fg }], children: infoMessage.message }),
111
- /* @__PURE__ */ jsx(Button, { mode: "contained", onPress: () => setInfoMessage(null), style: styles.overlayButton, buttonColor: colors.primary, textColor: colors.onPrimary, children: "OK" })
175
+ infoMessage && /* @__PURE__ */ jsx2(Portal, { children: /* @__PURE__ */ jsx2(View2, { style: styles2.overlay, children: /* @__PURE__ */ jsxs2(View2, { style: [styles2.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
176
+ /* @__PURE__ */ jsx2(Icon2, { source: "information-outline", size: 64, color: colors.secondary }),
177
+ /* @__PURE__ */ jsx2(Text2, { variant: "headlineLarge", style: [styles2.overlayTitle, { color: colors.secondary }], children: infoMessage.title }),
178
+ /* @__PURE__ */ jsx2(Text2, { variant: "bodyLarge", style: [styles2.overlayBody, { color: fg }], children: infoMessage.message }),
179
+ /* @__PURE__ */ jsx2(Button, { mode: "contained", onPress: () => setInfoMessage(null), style: styles2.overlayButton, buttonColor: colors.primary, textColor: colors.onPrimary, children: "OK" })
112
180
  ] }) }) })
113
181
  ] });
114
182
  }
115
- var styles = StyleSheet.create({
183
+ var styles2 = StyleSheet2.create({
116
184
  content: {
117
185
  gap: 24,
118
186
  paddingVertical: 20
@@ -200,17 +268,118 @@ var styles = StyleSheet.create({
200
268
  }
201
269
  });
202
270
 
271
+ // src/BaseStatsScreen.tsx
272
+ import { useAutoPaperTheme as useAutoPaperTheme3 } from "@rific/auto-paper";
273
+ import { Button as Button2, IconButton } from "@rific/feedback-press";
274
+ import { useState as useState2 } from "react";
275
+ import { ScrollView as ScrollView2, StyleSheet as StyleSheet3, View as View3 } from "react-native";
276
+ import { Icon as Icon3, Portal as Portal2, Text as Text3 } from "react-native-paper";
277
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
278
+ function BaseStatsScreen({ rotation = 0, title = "Achievements", titleVariant = "headlineSmall", onBack, insets, onReset, resetLabel = "Reset All Stats", resetConfirmTitle = "Reset Everything?", resetConfirmBody = "This permanently erases all stats and achievements. This cannot be undone.", fg: fgOverride, bg: bgOverride, cardBg: cardBgOverride, accentColor: accentColorOverride, children }) {
279
+ const { dark, colors } = useAutoPaperTheme3();
280
+ const [confirmResetVisible, setConfirmResetVisible] = useState2(false);
281
+ const showReset = !!onReset;
282
+ const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
283
+ const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
284
+ const cardBg = cardBgOverride ?? (dark ? "#111111" : "#F2F2F2");
285
+ const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
286
+ const accentColor = accentColorOverride ?? colors.secondary;
287
+ return /* @__PURE__ */ jsxs3(View3, { style: [styles3.container, { backgroundColor: bg }], children: [
288
+ /* @__PURE__ */ jsxs3(View3, { style: [styles3.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
289
+ /* @__PURE__ */ jsx3(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
290
+ /* @__PURE__ */ jsx3(Text3, { variant: titleVariant, style: [styles3.title, { color: fg }], children: title })
291
+ ] }),
292
+ /* @__PURE__ */ jsxs3(ScrollView2, { style: styles3.scrollView, contentContainerStyle: [styles3.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
293
+ children,
294
+ showReset && /* @__PURE__ */ jsx3(Button2, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.secondary, textColor: colors.onSecondary, style: styles3.resetButton, children: resetLabel })
295
+ ] }),
296
+ confirmResetVisible && /* @__PURE__ */ jsx3(Portal2, { children: /* @__PURE__ */ jsx3(View3, { style: [styles3.overlay, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: /* @__PURE__ */ jsxs3(View3, { style: [styles3.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }], children: [
297
+ /* @__PURE__ */ jsx3(Icon3, { source: "alert-outline", size: 64, color: accentColor }),
298
+ /* @__PURE__ */ jsx3(Text3, { variant: "headlineLarge", style: [styles3.overlayTitle, { color: accentColor }], children: resetConfirmTitle }),
299
+ /* @__PURE__ */ jsx3(Text3, { variant: "bodyLarge", style: [styles3.overlayBody, { color: fg }], children: resetConfirmBody }),
300
+ /* @__PURE__ */ jsx3(Button2, { mode: "contained", onPress: () => setConfirmResetVisible(false), style: styles3.overlayButton, children: "Cancel" }),
301
+ /* @__PURE__ */ jsx3(
302
+ Button2,
303
+ {
304
+ mode: "contained",
305
+ onPress: () => {
306
+ onReset?.();
307
+ setConfirmResetVisible(false);
308
+ },
309
+ style: styles3.overlayButton,
310
+ buttonColor: colors.secondary,
311
+ textColor: colors.onSecondary,
312
+ children: "Reset"
313
+ }
314
+ )
315
+ ] }) }) })
316
+ ] });
317
+ }
318
+ var styles3 = StyleSheet3.create({
319
+ container: {
320
+ flex: 1
321
+ },
322
+ content: {
323
+ gap: 12,
324
+ paddingHorizontal: 20
325
+ },
326
+ header: {
327
+ alignItems: "center",
328
+ flexDirection: "row",
329
+ gap: 4,
330
+ paddingBottom: 8
331
+ },
332
+ // Same overlay shape BaseSettingsDialog's own info overlay uses (backdrop + centered card),
333
+ // rotated in place the same way rather than via Portal-root transform for the same reason — see
334
+ // that component's own identical styles for the full rationale.
335
+ overlay: {
336
+ alignItems: "center",
337
+ backgroundColor: "rgba(0,0,0,0.72)",
338
+ bottom: 0,
339
+ justifyContent: "center",
340
+ left: 0,
341
+ position: "absolute",
342
+ right: 0,
343
+ top: 0
344
+ },
345
+ overlayBody: { textAlign: "center" },
346
+ overlayButton: { width: 160 },
347
+ overlayCard: {
348
+ alignItems: "center",
349
+ borderRadius: 20,
350
+ borderWidth: 1,
351
+ gap: 16,
352
+ maxWidth: 360,
353
+ padding: 32
354
+ },
355
+ overlayTitle: { fontWeight: "bold", marginBottom: -8 },
356
+ resetButton: {
357
+ marginTop: 16
358
+ },
359
+ // Bounds the ScrollView to the space `container`'s flex:1 actually gives it — without this, a
360
+ // ScrollView with only a contentContainerStyle isn't reliably height-constrained on native (it
361
+ // can render at its full unclipped content height instead of the screen's), which leaves the
362
+ // header's back button touch target unreliable underneath it.
363
+ scrollView: {
364
+ flex: 1
365
+ },
366
+ title: {
367
+ flexShrink: 1,
368
+ fontWeight: "bold"
369
+ }
370
+ });
371
+
203
372
  // src/CornerActionButtons.tsx
204
- import { IconButton } from "@rific/feedback-press";
205
- import { StyleSheet as StyleSheet2 } from "react-native";
206
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
373
+ import { IconButton as IconButton2 } from "@rific/feedback-press";
374
+ import { StyleSheet as StyleSheet4 } from "react-native";
375
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
207
376
  function CornerActionButtons({ onBack, onSettings, fg, insets }) {
208
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
209
- /* @__PURE__ */ jsx2(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, style: [styles2.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: "Back" }),
210
- /* @__PURE__ */ jsx2(IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles2.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
377
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
378
+ /* @__PURE__ */ jsx4(IconButton2, { icon: "arrow-left", iconColor: fg, size: 24, style: [styles4.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: "Back" }),
379
+ /* @__PURE__ */ jsx4(IconButton2, { icon: "cog", iconColor: fg, size: 24, style: [styles4.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
211
380
  ] });
212
381
  }
213
- var styles2 = StyleSheet2.create({
382
+ var styles4 = StyleSheet4.create({
214
383
  back: {
215
384
  position: "absolute"
216
385
  },
@@ -227,34 +396,34 @@ var MONO_FONT = Platform2.select({ ios: "Menlo", android: "monospace", default:
227
396
  import { defaultColors, getContrastColor } from "@rific/auto-paper";
228
397
  import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
229
398
  import { clamp } from "@tastic/core";
230
- import { ScrollView as ScrollView2, StyleSheet as StyleSheet4, useWindowDimensions as useWindowDimensions2, View as View4 } from "react-native";
231
- import { Icon as Icon2, Text as Text2 } from "react-native-paper";
399
+ import { ScrollView as ScrollView3, StyleSheet as StyleSheet6, useWindowDimensions as useWindowDimensions2, View as View6 } from "react-native";
400
+ import { Icon as Icon4, Text as Text4 } from "react-native-paper";
232
401
 
233
402
  // src/PopoverBody.tsx
234
- import { StyleSheet as StyleSheet3, View as View2 } from "react-native";
235
- import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
403
+ import { StyleSheet as StyleSheet5, View as View4 } from "react-native";
404
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
236
405
  var CARET_SIZE = 7;
237
406
  var CARET_RING_OFFSET = 2;
238
407
  var CARET_DIP = 1;
239
408
  function PopoverBody({ visible, children, align = "center", verticalAlign = "below", caretColor, caretBorderColor, caretBorderWidth = 1, triggerSize = 0 }) {
240
409
  if (!visible) return null;
241
410
  const above = verticalAlign === "above";
242
- const alignStyle = align === "left" ? styles3.contentLeft : align === "right" ? styles3.contentRight : styles3.contentCenter;
243
- const verticalStyle = above ? styles3.contentAbove : styles3.contentBelow;
411
+ const alignStyle = align === "left" ? styles5.contentLeft : align === "right" ? styles5.contentRight : styles5.contentCenter;
412
+ const verticalStyle = above ? styles5.contentAbove : styles5.contentBelow;
244
413
  const ringSize = CARET_SIZE + caretBorderWidth;
245
414
  const caretOffset = (halfFootprint) => align === "right" ? { right: triggerSize / 2 - halfFootprint } : { left: triggerSize / 2 - halfFootprint };
246
415
  const caretFill = above ? "borderTopColor" : "borderBottomColor";
247
416
  const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
248
417
  const caretEdge = (size) => above ? { bottom: -size + CARET_DIP } : { top: -size + CARET_DIP };
249
- return /* @__PURE__ */ jsxs3(View2, { style: [styles3.content, alignStyle, verticalStyle], children: [
418
+ return /* @__PURE__ */ jsxs5(View4, { style: [styles5.content, alignStyle, verticalStyle], children: [
250
419
  children,
251
- caretColor && /* @__PURE__ */ jsxs3(Fragment3, { children: [
252
- /* @__PURE__ */ jsx3(View2, { style: [styles3.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
253
- /* @__PURE__ */ jsx3(View2, { style: [styles3.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
420
+ caretColor && /* @__PURE__ */ jsxs5(Fragment3, { children: [
421
+ /* @__PURE__ */ jsx5(View4, { style: [styles5.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
422
+ /* @__PURE__ */ jsx5(View4, { style: [styles5.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
254
423
  ] })
255
424
  ] });
256
425
  }
257
- var styles3 = StyleSheet3.create({
426
+ var styles5 = StyleSheet5.create({
258
427
  caret: {
259
428
  borderLeftColor: "transparent",
260
429
  borderRightColor: "transparent",
@@ -297,17 +466,17 @@ var styles3 = StyleSheet3.create({
297
466
  });
298
467
 
299
468
  // src/useAutoAlign.ts
300
- import { useCallback, useEffect, useRef, useState as useState2 } from "react";
469
+ import { useCallback, useEffect, useRef, useState as useState3 } from "react";
301
470
  import { useWindowDimensions } from "react-native";
302
471
  var EDGE_MARGIN = 12;
303
472
  function useAutoAlign(open, contentWidth, contentHeight) {
304
473
  const triggerRef = useRef(null);
305
474
  const { width: windowWidth, height: windowHeight } = useWindowDimensions();
306
- const [align, setAlign] = useState2("center");
307
- const [verticalAlign, setVerticalAlign] = useState2("below");
308
- const [maxHeight, setMaxHeight] = useState2(contentHeight);
309
- const [measured, setMeasured] = useState2(false);
310
- const [prevOpen, setPrevOpen] = useState2(open);
475
+ const [align, setAlign] = useState3("center");
476
+ const [verticalAlign, setVerticalAlign] = useState3("below");
477
+ const [maxHeight, setMaxHeight] = useState3(contentHeight);
478
+ const [measured, setMeasured] = useState3(false);
479
+ const [prevOpen, setPrevOpen] = useState3(open);
311
480
  if (open !== prevOpen) {
312
481
  setPrevOpen(open);
313
482
  if (open) setMeasured(false);
@@ -335,7 +504,7 @@ function useAutoAlign(open, contentWidth, contentHeight) {
335
504
  }
336
505
 
337
506
  // src/InlineColorPicker.tsx
338
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
507
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
339
508
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
340
509
  var DEFAULT_SIZE = 48;
341
510
  var SWATCH_SIZE = 28;
@@ -361,13 +530,13 @@ function InlineColorPicker({ id, host, value, onChange, swatches = defaultColors
361
530
  const align = alignOverride ?? autoAlign;
362
531
  const contrastColor = getContrastColor(value);
363
532
  const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
364
- return /* @__PURE__ */ jsxs4(View4, { style: [styles4.anchor, open && styles4.anchorOpen], children: [
365
- /* @__PURE__ */ jsx4(TouchableRipple2, { onPress: () => host.toggle(id), borderless: true, style: [styles4.trigger, { backgroundColor: value, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx4(View4, { ref: triggerRef, collapsable: false, style: styles4.triggerMeasure, children: tag ? /* @__PURE__ */ jsx4(Text2, { style: [styles4.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx4(Icon2, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
366
- /* @__PURE__ */ jsx4(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: value, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx4(ScrollView2, { style: [styles4.swatches, { backgroundColor: menuBg, borderColor: value, width: swatchesWidth, maxHeight }], contentContainerStyle: styles4.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
533
+ return /* @__PURE__ */ jsxs6(View6, { style: [styles6.anchor, open && styles6.anchorOpen], children: [
534
+ /* @__PURE__ */ jsx6(TouchableRipple2, { onPress: () => host.toggle(id), borderless: true, style: [styles6.trigger, { backgroundColor: value, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx6(View6, { ref: triggerRef, collapsable: false, style: styles6.triggerMeasure, children: tag ? /* @__PURE__ */ jsx6(Text4, { style: [styles6.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx6(Icon4, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
535
+ /* @__PURE__ */ jsx6(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: value, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx6(ScrollView3, { style: [styles6.swatches, { backgroundColor: menuBg, borderColor: value, width: swatchesWidth, maxHeight }], contentContainerStyle: styles6.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
367
536
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
368
537
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
369
538
  const swappable = taken && allowSwapTaken;
370
- return /* @__PURE__ */ jsx4(
539
+ return /* @__PURE__ */ jsx6(
371
540
  TouchableRipple2,
372
541
  {
373
542
  disabled: taken && !swappable,
@@ -376,15 +545,15 @@ function InlineColorPicker({ id, host, value, onChange, swatches = defaultColors
376
545
  if (autoDismiss) host.close();
377
546
  },
378
547
  borderless: true,
379
- style: [styles4.swatch, { backgroundColor: swatch.value }, selected && styles4.swatchSelected, taken && !swappable && styles4.swatchTaken],
380
- children: selected ? /* @__PURE__ */ jsx4(Icon2, { source: "check", size: 16, color: getContrastColor(swatch.value) }) : swappable ? /* @__PURE__ */ jsx4(Icon2, { source: "swap-horizontal", size: 16, color: getContrastColor(swatch.value) }) : taken ? /* @__PURE__ */ jsx4(Icon2, { source: "close", size: 16, color: getContrastColor(swatch.value) }) : /* @__PURE__ */ jsx4(View4, {})
548
+ style: [styles6.swatch, { backgroundColor: swatch.value }, selected && styles6.swatchSelected, taken && !swappable && styles6.swatchTaken],
549
+ children: selected ? /* @__PURE__ */ jsx6(Icon4, { source: "check", size: 16, color: getContrastColor(swatch.value) }) : swappable ? /* @__PURE__ */ jsx6(Icon4, { source: "swap-horizontal", size: 16, color: getContrastColor(swatch.value) }) : taken ? /* @__PURE__ */ jsx6(Icon4, { source: "close", size: 16, color: getContrastColor(swatch.value) }) : /* @__PURE__ */ jsx6(View6, {})
381
550
  },
382
551
  swatch.value
383
552
  );
384
553
  }) }) })
385
554
  ] });
386
555
  }
387
- var styles4 = StyleSheet4.create({
556
+ var styles6 = StyleSheet6.create({
388
557
  anchor: {
389
558
  position: "relative"
390
559
  },
@@ -458,9 +627,9 @@ var styles4 = StyleSheet4.create({
458
627
  // src/LabeledDropdown.tsx
459
628
  import { getContrastColor as getContrastColor2 } from "@rific/auto-paper";
460
629
  import { TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
461
- import { ScrollView as ScrollView3, StyleSheet as StyleSheet5, View as View5 } from "react-native";
462
- import { Icon as Icon3, Text as Text3 } from "react-native-paper";
463
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
630
+ import { ScrollView as ScrollView4, StyleSheet as StyleSheet7, View as View7 } from "react-native";
631
+ import { Icon as Icon5, Text as Text5 } from "react-native-paper";
632
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
464
633
  var TRIGGER_HEIGHT = 28;
465
634
  var POPOVER_WIDTH = 180;
466
635
  var ROW_HEIGHT = 36;
@@ -483,22 +652,22 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
483
652
  onChange(option.value);
484
653
  host.close();
485
654
  };
486
- return /* @__PURE__ */ jsxs5(View5, { style: [styles5.anchor, open && styles5.anchorOpen], children: [
487
- /* @__PURE__ */ jsx5(TouchableRipple3, { onPress: () => host.toggle(id), style: styles5.trigger, children: /* @__PURE__ */ jsxs5(View5, { ref: triggerRef, collapsable: false, style: styles5.triggerInner, children: [
488
- /* @__PURE__ */ jsx5(Text3, { style: [styles5.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
489
- /* @__PURE__ */ jsx5(Icon3, { source: "menu-down", size: 16, color })
655
+ return /* @__PURE__ */ jsxs7(View7, { style: [styles7.anchor, open && styles7.anchorOpen], children: [
656
+ /* @__PURE__ */ jsx7(TouchableRipple3, { onPress: () => host.toggle(id), style: styles7.trigger, children: /* @__PURE__ */ jsxs7(View7, { ref: triggerRef, collapsable: false, style: styles7.triggerInner, children: [
657
+ /* @__PURE__ */ jsx7(Text5, { style: [styles7.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
658
+ /* @__PURE__ */ jsx7(Icon5, { source: "menu-down", size: 16, color })
490
659
  ] }) }),
491
- /* @__PURE__ */ jsx5(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ jsx5(ScrollView3, { style: [styles5.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles5.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
660
+ /* @__PURE__ */ jsx7(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ jsx7(ScrollView4, { style: [styles7.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles7.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
492
661
  const isSelected = option.value === value;
493
662
  const onColor = getContrastColor2(color);
494
- return /* @__PURE__ */ jsx5(TouchableRipple3, { onPress: () => handleSelect(option), style: [styles5.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs5(View5, { style: styles5.rowInner, children: [
495
- option.icon && /* @__PURE__ */ jsx5(Icon3, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
496
- /* @__PURE__ */ jsx5(Text3, { style: [styles5.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
663
+ return /* @__PURE__ */ jsx7(TouchableRipple3, { onPress: () => handleSelect(option), style: [styles7.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs7(View7, { style: styles7.rowInner, children: [
664
+ option.icon && /* @__PURE__ */ jsx7(Icon5, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
665
+ /* @__PURE__ */ jsx7(Text5, { style: [styles7.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
497
666
  ] }) }, option.value);
498
667
  }) }) })
499
668
  ] });
500
669
  }
501
- var styles5 = StyleSheet5.create({
670
+ var styles7 = StyleSheet7.create({
502
671
  anchor: {
503
672
  position: "relative"
504
673
  },
@@ -556,25 +725,25 @@ function loadSkiaWeb() {
556
725
  }
557
726
 
558
727
  // src/PressAwayOverlay.tsx
559
- import { Pressable, StyleSheet as StyleSheet6 } from "react-native";
560
- import { jsx as jsx6 } from "react/jsx-runtime";
728
+ import { Pressable, StyleSheet as StyleSheet8 } from "react-native";
729
+ import { jsx as jsx8 } from "react/jsx-runtime";
561
730
  function PressAwayOverlay({ active, onPress, style }) {
562
731
  if (!active) return null;
563
- return /* @__PURE__ */ jsx6(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet6.absoluteFill, style] });
732
+ return /* @__PURE__ */ jsx8(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet8.absoluteFill, style] });
564
733
  }
565
734
 
566
735
  // src/ReadyButton.tsx
567
736
  import { TouchableRipple as TouchableRipple4 } from "@rific/feedback-press";
568
- import { StyleSheet as StyleSheet7 } from "react-native";
569
- import { Text as Text4 } from "react-native-paper";
570
- import { jsx as jsx7 } from "react/jsx-runtime";
737
+ import { StyleSheet as StyleSheet9 } from "react-native";
738
+ import { Text as Text6 } from "react-native-paper";
739
+ import { jsx as jsx9 } from "react/jsx-runtime";
571
740
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
572
- const readyButtonStyle = [styles6.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
741
+ const readyButtonStyle = [styles8.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
573
742
  const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
574
743
  const labelColorStyle = { color: ready ? "#000000" : color };
575
- return /* @__PURE__ */ jsx7(TouchableRipple4, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx7(Text4, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
744
+ return /* @__PURE__ */ jsx9(TouchableRipple4, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx9(Text6, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
576
745
  }
577
- var styles6 = StyleSheet7.create({
746
+ var styles8 = StyleSheet9.create({
578
747
  readyButton: {
579
748
  alignItems: "center",
580
749
  borderRadius: 12,
@@ -588,20 +757,20 @@ var styles6 = StyleSheet7.create({
588
757
 
589
758
  // src/SectionedDropdown.tsx
590
759
  import { getBlendedColor, getColorRoles } from "@rific/auto-paper";
591
- import { IconButton as IconButton2, TouchableRipple as TouchableRipple5 } from "@rific/feedback-press";
592
- import { ScrollView as ScrollView4, StyleSheet as StyleSheet8, View as View6 } from "react-native";
593
- import { Icon as Icon4, Text as Text5 } from "react-native-paper";
760
+ import { IconButton as IconButton3, TouchableRipple as TouchableRipple5 } from "@rific/feedback-press";
761
+ import { ScrollView as ScrollView5, StyleSheet as StyleSheet10, View as View8 } from "react-native";
762
+ import { Icon as Icon6, Text as Text7 } from "react-native-paper";
594
763
 
595
764
  // src/TriggerGaugeHost.tsx
596
765
  import { lazy, Suspense } from "react";
597
- import { jsx as jsx8 } from "react/jsx-runtime";
766
+ import { jsx as jsx10 } from "react/jsx-runtime";
598
767
  var LazyTriggerGauge = lazy(() => loadSkiaWeb().then(() => import("./TriggerGauge-2OJOOF6I.mjs").then((m) => ({ default: m.TriggerGauge }))));
599
768
  function TriggerGaugeHost(props) {
600
- return /* @__PURE__ */ jsx8(Suspense, { fallback: null, children: /* @__PURE__ */ jsx8(LazyTriggerGauge, { ...props }) });
769
+ return /* @__PURE__ */ jsx10(Suspense, { fallback: null, children: /* @__PURE__ */ jsx10(LazyTriggerGauge, { ...props }) });
601
770
  }
602
771
 
603
772
  // src/SectionedDropdown.tsx
604
- import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
773
+ import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
605
774
  var MENU_BORDER_WIDTH = 1;
606
775
  var TRIGGER_SIZE = 44;
607
776
  var MENU_MIN_WIDTH = 200;
@@ -647,17 +816,17 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
647
816
  }
648
817
  gaugeOffset += section.options.length;
649
818
  }
650
- const anchorStyle = [styles7.anchor, open && styles7.anchorOpen];
819
+ const anchorStyle = [styles9.anchor, open && styles9.anchorOpen];
651
820
  const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
652
821
  const dividerStyle = { backgroundColor: menuBorderColor };
653
- return /* @__PURE__ */ jsxs6(View6, { style: anchorStyle, children: [
654
- /* @__PURE__ */ jsxs6(View6, { ref: triggerRef, collapsable: false, style: styles7.triggerBox, children: [
655
- /* @__PURE__ */ jsx9(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
656
- /* @__PURE__ */ jsx9(IconButton2, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
822
+ return /* @__PURE__ */ jsxs8(View8, { style: anchorStyle, children: [
823
+ /* @__PURE__ */ jsxs8(View8, { ref: triggerRef, collapsable: false, style: styles9.triggerBox, children: [
824
+ /* @__PURE__ */ jsx11(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
825
+ /* @__PURE__ */ jsx11(IconButton3, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
657
826
  ] }),
658
- /* @__PURE__ */ jsx9(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx9(ScrollView4, { style: [styles7.menu, menuStyle], contentContainerStyle: styles7.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs6(View6, { children: [
659
- sectionIndex > 0 && /* @__PURE__ */ jsx9(View6, { style: [styles7.divider, dividerStyle] }),
660
- section.kind === "single" ? /* @__PURE__ */ jsx9(
827
+ /* @__PURE__ */ jsx11(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx11(ScrollView5, { style: [styles9.menu, menuStyle], contentContainerStyle: styles9.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs8(View8, { children: [
828
+ sectionIndex > 0 && /* @__PURE__ */ jsx11(View8, { style: [styles9.divider, dividerStyle] }),
829
+ section.kind === "single" ? /* @__PURE__ */ jsx11(
661
830
  SingleSection,
662
831
  {
663
832
  section,
@@ -670,7 +839,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
670
839
  if (autoDismiss) host.close();
671
840
  }
672
841
  }
673
- ) : /* @__PURE__ */ jsx9(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
842
+ ) : /* @__PURE__ */ jsx11(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
674
843
  ] }, section.id)) }) })
675
844
  ] });
676
845
  }
@@ -682,17 +851,17 @@ function SingleSection({
682
851
  labelFontFamily,
683
852
  onSelect
684
853
  }) {
685
- return /* @__PURE__ */ jsx9(Fragment4, { children: section.options.map((option) => {
854
+ return /* @__PURE__ */ jsx11(Fragment4, { children: section.options.map((option) => {
686
855
  const selected = option.value === section.value;
687
856
  const rowColor = selected ? onAccentColor : mutedColor;
688
- const itemStyle = [styles7.item, selected && { backgroundColor: accentColor }];
857
+ const itemStyle = [styles9.item, selected && { backgroundColor: accentColor }];
689
858
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
690
859
  const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
691
- return /* @__PURE__ */ jsx9(TouchableRipple5, { onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs6(View6, { style: styles7.itemRow, children: [
692
- option.icon && /* @__PURE__ */ jsx9(Icon4, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
693
- /* @__PURE__ */ jsxs6(View6, { style: styles7.itemLabelColumn, children: [
694
- /* @__PURE__ */ jsx9(Text5, { variant: "labelLarge", style: labelStyle, children: option.label }),
695
- option.description && /* @__PURE__ */ jsx9(Text5, { variant: "labelSmall", style: descriptionStyle, children: option.description })
860
+ return /* @__PURE__ */ jsx11(TouchableRipple5, { onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs8(View8, { style: styles9.itemRow, children: [
861
+ option.icon && /* @__PURE__ */ jsx11(Icon6, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
862
+ /* @__PURE__ */ jsxs8(View8, { style: styles9.itemLabelColumn, children: [
863
+ /* @__PURE__ */ jsx11(Text7, { variant: "labelLarge", style: labelStyle, children: option.label }),
864
+ option.description && /* @__PURE__ */ jsx11(Text7, { variant: "labelSmall", style: descriptionStyle, children: option.description })
696
865
  ] })
697
866
  ] }) }, option.value);
698
867
  }) });
@@ -706,23 +875,23 @@ function MultiSection({
706
875
  allClearLabels
707
876
  }) {
708
877
  const allSelected = section.value.length === section.options.length;
709
- return /* @__PURE__ */ jsxs6(Fragment4, { children: [
878
+ return /* @__PURE__ */ jsxs8(Fragment4, { children: [
710
879
  section.options.map((option, i) => {
711
880
  const selected = section.value.includes(option.value);
712
881
  const rowColor = selected ? onAccentColor : mutedColor;
713
882
  const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
714
883
  const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
715
- const itemStyle = [styles7.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
884
+ const itemStyle = [styles9.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
716
885
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
717
- return /* @__PURE__ */ jsx9(TouchableRipple5, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs6(View6, { style: styles7.itemRow, children: [
718
- option.icon && /* @__PURE__ */ jsx9(Icon4, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
719
- /* @__PURE__ */ jsx9(Text5, { variant: "labelLarge", style: [styles7.itemLabelFlex, labelStyle], children: option.label })
886
+ return /* @__PURE__ */ jsx11(TouchableRipple5, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs8(View8, { style: styles9.itemRow, children: [
887
+ option.icon && /* @__PURE__ */ jsx11(Icon6, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
888
+ /* @__PURE__ */ jsx11(Text7, { variant: "labelLarge", style: [styles9.itemLabelFlex, labelStyle], children: option.label })
720
889
  ] }) }, option.value);
721
890
  }),
722
- section.allClear && /* @__PURE__ */ jsx9(TouchableRipple5, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles7.item, children: /* @__PURE__ */ jsx9(Text5, { variant: "labelLarge", style: [styles7.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
891
+ section.allClear && /* @__PURE__ */ jsx11(TouchableRipple5, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles9.item, children: /* @__PURE__ */ jsx11(Text7, { variant: "labelLarge", style: [styles9.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
723
892
  ] });
724
893
  }
725
- var styles7 = StyleSheet8.create({
894
+ var styles9 = StyleSheet10.create({
726
895
  allClearLabel: {
727
896
  fontWeight: "bold",
728
897
  textAlign: "center"
@@ -739,7 +908,7 @@ var styles7 = StyleSheet8.create({
739
908
  zIndex: 100
740
909
  },
741
910
  divider: {
742
- height: StyleSheet8.hairlineWidth,
911
+ height: StyleSheet10.hairlineWidth,
743
912
  marginVertical: 4
744
913
  },
745
914
  item: {
@@ -787,21 +956,21 @@ var styles7 = StyleSheet8.create({
787
956
  });
788
957
 
789
958
  // src/SharedActionBand.tsx
790
- import { IconButton as IconButton3 } from "@rific/feedback-press";
791
- import { StyleSheet as StyleSheet9, View as View7 } from "react-native";
792
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
959
+ import { IconButton as IconButton4 } from "@rific/feedback-press";
960
+ import { StyleSheet as StyleSheet11, View as View9 } from "react-native";
961
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
793
962
  function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
794
- return /* @__PURE__ */ jsxs7(View7, { style: [styles8.column, popoverOpen && styles8.columnOpen], children: [
795
- /* @__PURE__ */ jsxs7(View7, { style: styles8.actionsRow, children: [
796
- /* @__PURE__ */ jsx10(IconButton3, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
797
- onRandomize && /* @__PURE__ */ jsx10(IconButton3, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
798
- onReset && /* @__PURE__ */ jsx10(IconButton3, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
799
- /* @__PURE__ */ jsx10(IconButton3, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
963
+ return /* @__PURE__ */ jsxs9(View9, { style: [styles10.column, popoverOpen && styles10.columnOpen], children: [
964
+ /* @__PURE__ */ jsxs9(View9, { style: styles10.actionsRow, children: [
965
+ /* @__PURE__ */ jsx12(IconButton4, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
966
+ onRandomize && /* @__PURE__ */ jsx12(IconButton4, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
967
+ onReset && /* @__PURE__ */ jsx12(IconButton4, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
968
+ /* @__PURE__ */ jsx12(IconButton4, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
800
969
  ] }),
801
970
  children
802
971
  ] });
803
972
  }
804
- var styles8 = StyleSheet9.create({
973
+ var styles10 = StyleSheet11.create({
805
974
  actionsRow: {
806
975
  alignItems: "center",
807
976
  flexDirection: "row",
@@ -816,10 +985,65 @@ var styles8 = StyleSheet9.create({
816
985
  }
817
986
  });
818
987
 
988
+ // src/StatRow.tsx
989
+ import { useAutoPaperTheme as useAutoPaperTheme4 } from "@rific/auto-paper";
990
+ import { StyleSheet as StyleSheet12, View as View10 } from "react-native";
991
+ import { Text as Text8 } from "react-native-paper";
992
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
993
+ function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }) {
994
+ const { dark } = useAutoPaperTheme4();
995
+ const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
996
+ const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
997
+ return /* @__PURE__ */ jsxs10(View10, { style: styles11.row, children: [
998
+ /* @__PURE__ */ jsx13(Text8, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
999
+ /* @__PURE__ */ jsx13(Text8, { variant: "bodyMedium", style: [styles11.value, { color: fg }], children: value })
1000
+ ] });
1001
+ }
1002
+ var styles11 = StyleSheet12.create({
1003
+ row: {
1004
+ alignItems: "center",
1005
+ flexDirection: "row",
1006
+ justifyContent: "space-between"
1007
+ },
1008
+ value: {
1009
+ fontWeight: "bold"
1010
+ }
1011
+ });
1012
+
1013
+ // src/StatSection.tsx
1014
+ import { useAutoPaperTheme as useAutoPaperTheme5 } from "@rific/auto-paper";
1015
+ import { StyleSheet as StyleSheet13, View as View11 } from "react-native";
1016
+ import { Text as Text9 } from "react-native-paper";
1017
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1018
+ function StatSection({ label, fg: fgOverride, sectionBg: sectionBgOverride, children }) {
1019
+ const { dark } = useAutoPaperTheme5();
1020
+ const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
1021
+ const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
1022
+ return /* @__PURE__ */ jsxs11(View11, { style: [styles12.section, { backgroundColor: sectionBg }], children: [
1023
+ /* @__PURE__ */ jsx14(Text9, { variant: "labelMedium", style: [styles12.label, { color: fg }], children: label }),
1024
+ children
1025
+ ] });
1026
+ }
1027
+ var styles12 = StyleSheet13.create({
1028
+ // Bold and full-contrast (fg), not the original fontless/fgMuted treatment — that read at the
1029
+ // same visual weight as StatRow's own row labels below it (both used the identical muted color;
1030
+ // letterSpacing/variant alone didn't separate them enough to read as "this is the group header"
1031
+ // at a glance). Matches AchievementRow's own title styling (bold + fg) below it in the same list.
1032
+ label: {
1033
+ fontWeight: "bold",
1034
+ letterSpacing: 2
1035
+ },
1036
+ section: {
1037
+ borderRadius: 12,
1038
+ gap: 8,
1039
+ padding: 16
1040
+ }
1041
+ });
1042
+
819
1043
  // src/usePopoverHost.ts
820
- import { useCallback as useCallback2, useState as useState3 } from "react";
1044
+ import { useCallback as useCallback2, useState as useState4 } from "react";
821
1045
  function usePopoverHost() {
822
- const [openId, setOpenId] = useState3(null);
1046
+ const [openId, setOpenId] = useState4(null);
823
1047
  const toggle = useCallback2((id) => {
824
1048
  setOpenId((prev) => prev === id ? null : id);
825
1049
  }, []);
@@ -827,10 +1051,13 @@ function usePopoverHost() {
827
1051
  return { openId, toggle, close };
828
1052
  }
829
1053
  export {
1054
+ AchievementRow,
830
1055
  BaseSettingsDialog,
1056
+ BaseStatsScreen,
831
1057
  CornerActionButtons,
832
1058
  InlineColorPicker,
833
1059
  LABELED_DROPDOWN_POPOVER_WIDTH,
1060
+ LOCKED_BADGE_COLOR,
834
1061
  LabeledDropdown,
835
1062
  MONO_FONT,
836
1063
  PopoverBody,
@@ -838,6 +1065,8 @@ export {
838
1065
  ReadyButton,
839
1066
  SectionedDropdown,
840
1067
  SharedActionBand,
1068
+ StatRow,
1069
+ StatSection,
841
1070
  TriggerGaugeHost,
842
1071
  getLabeledDropdownContentHeight,
843
1072
  loadSkiaWeb,