@tastic/hud 0.2.0 → 0.4.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 }) {
8
+ const { dark } = useAutoPaperTheme();
9
+ const fg = dark ? "#FFFFFF" : "#000000";
10
+ const fgMuted = dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)";
11
+ const sectionBg = 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,6 +268,125 @@ 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", onBack, insets, onReset, resetLabel = "Reset All Stats", resetConfirmTitle = "Reset Everything?", resetConfirmBody = "This permanently erases all stats and achievements. This cannot be undone.", children }) {
279
+ const { dark, colors } = useAutoPaperTheme3();
280
+ const [confirmResetVisible, setConfirmResetVisible] = useState2(false);
281
+ const showReset = !!onReset;
282
+ const fg = dark ? "#FFFFFF" : "#000000";
283
+ const bg = dark ? "#000000" : "#FFFFFF";
284
+ const cardBg = dark ? "#111111" : "#F2F2F2";
285
+ const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
286
+ return /* @__PURE__ */ jsxs3(View3, { style: [styles3.container, { backgroundColor: bg }], children: [
287
+ /* @__PURE__ */ jsxs3(View3, { style: [styles3.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
288
+ /* @__PURE__ */ jsx3(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
289
+ /* @__PURE__ */ jsx3(Text3, { variant: "displaySmall", style: [styles3.title, { color: fg }], children: title })
290
+ ] }),
291
+ /* @__PURE__ */ jsxs3(ScrollView2, { style: styles3.scrollView, contentContainerStyle: [styles3.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
292
+ children,
293
+ showReset && /* @__PURE__ */ jsx3(Button2, { mode: "outlined", onPress: () => setConfirmResetVisible(true), textColor: colors.secondary, style: styles3.resetButton, children: resetLabel })
294
+ ] }),
295
+ 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: [
296
+ /* @__PURE__ */ jsx3(Icon3, { source: "alert-outline", size: 64, color: colors.secondary }),
297
+ /* @__PURE__ */ jsx3(Text3, { variant: "headlineLarge", style: [styles3.overlayTitle, { color: colors.secondary }], children: resetConfirmTitle }),
298
+ /* @__PURE__ */ jsx3(Text3, { variant: "bodyLarge", style: [styles3.overlayBody, { color: fg }], children: resetConfirmBody }),
299
+ /* @__PURE__ */ jsx3(Button2, { mode: "contained", onPress: () => setConfirmResetVisible(false), style: styles3.overlayButton, children: "Cancel" }),
300
+ /* @__PURE__ */ jsx3(
301
+ Button2,
302
+ {
303
+ mode: "contained",
304
+ onPress: () => {
305
+ onReset?.();
306
+ setConfirmResetVisible(false);
307
+ },
308
+ style: styles3.overlayButton,
309
+ buttonColor: colors.secondary,
310
+ textColor: colors.onSecondary,
311
+ children: "Reset"
312
+ }
313
+ )
314
+ ] }) }) })
315
+ ] });
316
+ }
317
+ var styles3 = StyleSheet3.create({
318
+ container: {
319
+ flex: 1
320
+ },
321
+ content: {
322
+ gap: 12,
323
+ paddingHorizontal: 20
324
+ },
325
+ header: {
326
+ alignItems: "center",
327
+ flexDirection: "row",
328
+ gap: 4,
329
+ paddingBottom: 8
330
+ },
331
+ // Same overlay shape BaseSettingsDialog's own info overlay uses (backdrop + centered card),
332
+ // rotated in place the same way rather than via Portal-root transform for the same reason — see
333
+ // that component's own identical styles for the full rationale.
334
+ overlay: {
335
+ alignItems: "center",
336
+ backgroundColor: "rgba(0,0,0,0.72)",
337
+ bottom: 0,
338
+ justifyContent: "center",
339
+ left: 0,
340
+ position: "absolute",
341
+ right: 0,
342
+ top: 0
343
+ },
344
+ overlayBody: { textAlign: "center" },
345
+ overlayButton: { width: 160 },
346
+ overlayCard: {
347
+ alignItems: "center",
348
+ borderRadius: 20,
349
+ borderWidth: 1,
350
+ gap: 16,
351
+ maxWidth: 360,
352
+ padding: 32
353
+ },
354
+ overlayTitle: { fontWeight: "bold", marginBottom: -8 },
355
+ resetButton: {
356
+ marginTop: 16
357
+ },
358
+ // Bounds the ScrollView to the space `container`'s flex:1 actually gives it — without this, a
359
+ // ScrollView with only a contentContainerStyle isn't reliably height-constrained on native (it
360
+ // can render at its full unclipped content height instead of the screen's), which leaves the
361
+ // header's back button touch target unreliable underneath it.
362
+ scrollView: {
363
+ flex: 1
364
+ },
365
+ title: {
366
+ flexShrink: 1,
367
+ fontWeight: "bold"
368
+ }
369
+ });
370
+
371
+ // src/CornerActionButtons.tsx
372
+ import { IconButton as IconButton2 } from "@rific/feedback-press";
373
+ import { StyleSheet as StyleSheet4 } from "react-native";
374
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
375
+ function CornerActionButtons({ onBack, onSettings, fg, insets }) {
376
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
377
+ /* @__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" }),
378
+ /* @__PURE__ */ jsx4(IconButton2, { icon: "cog", iconColor: fg, size: 24, style: [styles4.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
379
+ ] });
380
+ }
381
+ var styles4 = StyleSheet4.create({
382
+ back: {
383
+ position: "absolute"
384
+ },
385
+ topRight: {
386
+ position: "absolute"
387
+ }
388
+ });
389
+
203
390
  // src/fonts.ts
204
391
  import { Platform as Platform2 } from "react-native";
205
392
  var MONO_FONT = Platform2.select({ ios: "Menlo", android: "monospace", default: "monospace" });
@@ -208,34 +395,34 @@ var MONO_FONT = Platform2.select({ ios: "Menlo", android: "monospace", default:
208
395
  import { defaultColors, getContrastColor } from "@rific/auto-paper";
209
396
  import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
210
397
  import { clamp } from "@tastic/core";
211
- import { ScrollView as ScrollView2, StyleSheet as StyleSheet3, useWindowDimensions as useWindowDimensions2, View as View4 } from "react-native";
212
- import { Icon as Icon2, Text as Text2 } from "react-native-paper";
398
+ import { ScrollView as ScrollView3, StyleSheet as StyleSheet6, useWindowDimensions as useWindowDimensions2, View as View6 } from "react-native";
399
+ import { Icon as Icon4, Text as Text4 } from "react-native-paper";
213
400
 
214
401
  // src/PopoverBody.tsx
215
- import { StyleSheet as StyleSheet2, View as View2 } from "react-native";
216
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
402
+ import { StyleSheet as StyleSheet5, View as View4 } from "react-native";
403
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
217
404
  var CARET_SIZE = 7;
218
405
  var CARET_RING_OFFSET = 2;
219
406
  var CARET_DIP = 1;
220
407
  function PopoverBody({ visible, children, align = "center", verticalAlign = "below", caretColor, caretBorderColor, caretBorderWidth = 1, triggerSize = 0 }) {
221
408
  if (!visible) return null;
222
409
  const above = verticalAlign === "above";
223
- const alignStyle = align === "left" ? styles2.contentLeft : align === "right" ? styles2.contentRight : styles2.contentCenter;
224
- const verticalStyle = above ? styles2.contentAbove : styles2.contentBelow;
410
+ const alignStyle = align === "left" ? styles5.contentLeft : align === "right" ? styles5.contentRight : styles5.contentCenter;
411
+ const verticalStyle = above ? styles5.contentAbove : styles5.contentBelow;
225
412
  const ringSize = CARET_SIZE + caretBorderWidth;
226
413
  const caretOffset = (halfFootprint) => align === "right" ? { right: triggerSize / 2 - halfFootprint } : { left: triggerSize / 2 - halfFootprint };
227
414
  const caretFill = above ? "borderTopColor" : "borderBottomColor";
228
415
  const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
229
416
  const caretEdge = (size) => above ? { bottom: -size + CARET_DIP } : { top: -size + CARET_DIP };
230
- return /* @__PURE__ */ jsxs2(View2, { style: [styles2.content, alignStyle, verticalStyle], children: [
417
+ return /* @__PURE__ */ jsxs5(View4, { style: [styles5.content, alignStyle, verticalStyle], children: [
231
418
  children,
232
- caretColor && /* @__PURE__ */ jsxs2(Fragment2, { children: [
233
- /* @__PURE__ */ jsx2(View2, { style: [styles2.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
234
- /* @__PURE__ */ jsx2(View2, { style: [styles2.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
419
+ caretColor && /* @__PURE__ */ jsxs5(Fragment3, { children: [
420
+ /* @__PURE__ */ jsx5(View4, { style: [styles5.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
421
+ /* @__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 }] })
235
422
  ] })
236
423
  ] });
237
424
  }
238
- var styles2 = StyleSheet2.create({
425
+ var styles5 = StyleSheet5.create({
239
426
  caret: {
240
427
  borderLeftColor: "transparent",
241
428
  borderRightColor: "transparent",
@@ -278,17 +465,17 @@ var styles2 = StyleSheet2.create({
278
465
  });
279
466
 
280
467
  // src/useAutoAlign.ts
281
- import { useCallback, useEffect, useRef, useState as useState2 } from "react";
468
+ import { useCallback, useEffect, useRef, useState as useState3 } from "react";
282
469
  import { useWindowDimensions } from "react-native";
283
470
  var EDGE_MARGIN = 12;
284
471
  function useAutoAlign(open, contentWidth, contentHeight) {
285
472
  const triggerRef = useRef(null);
286
473
  const { width: windowWidth, height: windowHeight } = useWindowDimensions();
287
- const [align, setAlign] = useState2("center");
288
- const [verticalAlign, setVerticalAlign] = useState2("below");
289
- const [maxHeight, setMaxHeight] = useState2(contentHeight);
290
- const [measured, setMeasured] = useState2(false);
291
- const [prevOpen, setPrevOpen] = useState2(open);
474
+ const [align, setAlign] = useState3("center");
475
+ const [verticalAlign, setVerticalAlign] = useState3("below");
476
+ const [maxHeight, setMaxHeight] = useState3(contentHeight);
477
+ const [measured, setMeasured] = useState3(false);
478
+ const [prevOpen, setPrevOpen] = useState3(open);
292
479
  if (open !== prevOpen) {
293
480
  setPrevOpen(open);
294
481
  if (open) setMeasured(false);
@@ -316,7 +503,7 @@ function useAutoAlign(open, contentWidth, contentHeight) {
316
503
  }
317
504
 
318
505
  // src/InlineColorPicker.tsx
319
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
506
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
320
507
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
321
508
  var DEFAULT_SIZE = 48;
322
509
  var SWATCH_SIZE = 28;
@@ -342,13 +529,13 @@ function InlineColorPicker({ id, host, value, onChange, swatches = defaultColors
342
529
  const align = alignOverride ?? autoAlign;
343
530
  const contrastColor = getContrastColor(value);
344
531
  const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
345
- return /* @__PURE__ */ jsxs3(View4, { style: [styles3.anchor, open && styles3.anchorOpen], children: [
346
- /* @__PURE__ */ jsx3(TouchableRipple2, { onPress: () => host.toggle(id), borderless: true, style: [styles3.trigger, { backgroundColor: value, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx3(View4, { ref: triggerRef, collapsable: false, style: styles3.triggerMeasure, children: tag ? /* @__PURE__ */ jsx3(Text2, { style: [styles3.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx3(Icon2, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
347
- /* @__PURE__ */ jsx3(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: value, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx3(ScrollView2, { style: [styles3.swatches, { backgroundColor: menuBg, borderColor: value, width: swatchesWidth, maxHeight }], contentContainerStyle: styles3.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
532
+ return /* @__PURE__ */ jsxs6(View6, { style: [styles6.anchor, open && styles6.anchorOpen], children: [
533
+ /* @__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 }) }) }),
534
+ /* @__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) => {
348
535
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
349
536
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
350
537
  const swappable = taken && allowSwapTaken;
351
- return /* @__PURE__ */ jsx3(
538
+ return /* @__PURE__ */ jsx6(
352
539
  TouchableRipple2,
353
540
  {
354
541
  disabled: taken && !swappable,
@@ -357,15 +544,15 @@ function InlineColorPicker({ id, host, value, onChange, swatches = defaultColors
357
544
  if (autoDismiss) host.close();
358
545
  },
359
546
  borderless: true,
360
- style: [styles3.swatch, { backgroundColor: swatch.value }, selected && styles3.swatchSelected, taken && !swappable && styles3.swatchTaken],
361
- children: selected ? /* @__PURE__ */ jsx3(Icon2, { source: "check", size: 16, color: getContrastColor(swatch.value) }) : swappable ? /* @__PURE__ */ jsx3(Icon2, { source: "swap-horizontal", size: 16, color: getContrastColor(swatch.value) }) : taken ? /* @__PURE__ */ jsx3(Icon2, { source: "close", size: 16, color: getContrastColor(swatch.value) }) : /* @__PURE__ */ jsx3(View4, {})
547
+ style: [styles6.swatch, { backgroundColor: swatch.value }, selected && styles6.swatchSelected, taken && !swappable && styles6.swatchTaken],
548
+ 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, {})
362
549
  },
363
550
  swatch.value
364
551
  );
365
552
  }) }) })
366
553
  ] });
367
554
  }
368
- var styles3 = StyleSheet3.create({
555
+ var styles6 = StyleSheet6.create({
369
556
  anchor: {
370
557
  position: "relative"
371
558
  },
@@ -436,31 +623,126 @@ var styles3 = StyleSheet3.create({
436
623
  }
437
624
  });
438
625
 
626
+ // src/LabeledDropdown.tsx
627
+ import { getContrastColor as getContrastColor2 } from "@rific/auto-paper";
628
+ import { TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
629
+ import { ScrollView as ScrollView4, StyleSheet as StyleSheet7, View as View7 } from "react-native";
630
+ import { Icon as Icon5, Text as Text5 } from "react-native-paper";
631
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
632
+ var TRIGGER_HEIGHT = 28;
633
+ var POPOVER_WIDTH = 180;
634
+ var ROW_HEIGHT = 36;
635
+ var LIST_PADDING = 8;
636
+ var LABELED_DROPDOWN_POPOVER_WIDTH = POPOVER_WIDTH;
637
+ function getLabeledDropdownContentHeight(optionCount) {
638
+ return LIST_PADDING * 2 + optionCount * ROW_HEIGHT;
639
+ }
640
+ function LabeledDropdown({ id, host, options, value, onChange, color, dark, align: forcedAlign, alignOverride }) {
641
+ const menuBg = dark ? "#000000" : "#FFFFFF";
642
+ const fg = dark ? "#FFFFFF" : "#000000";
643
+ const open = host.openId === id;
644
+ const selected = options.find((o) => o.value === value) ?? null;
645
+ const contentHeight = LIST_PADDING * 2 + options.length * ROW_HEIGHT;
646
+ const auto = useAutoAlign(open, POPOVER_WIDTH, contentHeight);
647
+ const placement = alignOverride ?? auto;
648
+ const { maxHeight, measured, triggerRef, verticalAlign } = placement;
649
+ const align = forcedAlign ?? placement.align;
650
+ const handleSelect = (option) => {
651
+ onChange(option.value);
652
+ host.close();
653
+ };
654
+ return /* @__PURE__ */ jsxs7(View7, { style: [styles7.anchor, open && styles7.anchorOpen], children: [
655
+ /* @__PURE__ */ jsx7(TouchableRipple3, { onPress: () => host.toggle(id), style: styles7.trigger, children: /* @__PURE__ */ jsxs7(View7, { ref: triggerRef, collapsable: false, style: styles7.triggerInner, children: [
656
+ /* @__PURE__ */ jsx7(Text5, { style: [styles7.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
657
+ /* @__PURE__ */ jsx7(Icon5, { source: "menu-down", size: 16, color })
658
+ ] }) }),
659
+ /* @__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) => {
660
+ const isSelected = option.value === value;
661
+ const onColor = getContrastColor2(color);
662
+ return /* @__PURE__ */ jsx7(TouchableRipple3, { onPress: () => handleSelect(option), style: [styles7.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs7(View7, { style: styles7.rowInner, children: [
663
+ option.icon && /* @__PURE__ */ jsx7(Icon5, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
664
+ /* @__PURE__ */ jsx7(Text5, { style: [styles7.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
665
+ ] }) }, option.value);
666
+ }) }) })
667
+ ] });
668
+ }
669
+ var styles7 = StyleSheet7.create({
670
+ anchor: {
671
+ position: "relative"
672
+ },
673
+ // React Native Web gives every position:'relative' view its own stacking context, so the
674
+ // elevation has to live on the anchor itself — see PopoverBody's own stacking-context notes.
675
+ anchorOpen: {
676
+ zIndex: 100
677
+ },
678
+ menu: {
679
+ borderRadius: 12,
680
+ borderWidth: 2,
681
+ elevation: 8,
682
+ shadowColor: "#000000",
683
+ shadowOffset: { height: 2, width: 0 },
684
+ shadowOpacity: 0.3,
685
+ shadowRadius: 8
686
+ },
687
+ menuContent: {
688
+ padding: LIST_PADDING
689
+ },
690
+ row: {
691
+ borderRadius: 8,
692
+ height: ROW_HEIGHT
693
+ },
694
+ rowInner: {
695
+ alignItems: "center",
696
+ flexDirection: "row",
697
+ flex: 1,
698
+ gap: 8,
699
+ paddingHorizontal: 12
700
+ },
701
+ rowLabel: {
702
+ flex: 1,
703
+ fontSize: 14
704
+ },
705
+ trigger: {
706
+ alignSelf: "flex-start"
707
+ },
708
+ triggerInner: {
709
+ alignItems: "center",
710
+ flexDirection: "row",
711
+ gap: 2,
712
+ height: TRIGGER_HEIGHT
713
+ },
714
+ triggerLabel: {
715
+ fontSize: 13,
716
+ fontWeight: "700",
717
+ letterSpacing: 1
718
+ }
719
+ });
720
+
439
721
  // src/loadSkiaWeb.ts
440
722
  function loadSkiaWeb() {
441
723
  return Promise.resolve();
442
724
  }
443
725
 
444
726
  // src/PressAwayOverlay.tsx
445
- import { Pressable, StyleSheet as StyleSheet4 } from "react-native";
446
- import { jsx as jsx4 } from "react/jsx-runtime";
727
+ import { Pressable, StyleSheet as StyleSheet8 } from "react-native";
728
+ import { jsx as jsx8 } from "react/jsx-runtime";
447
729
  function PressAwayOverlay({ active, onPress, style }) {
448
730
  if (!active) return null;
449
- return /* @__PURE__ */ jsx4(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet4.absoluteFill, style] });
731
+ return /* @__PURE__ */ jsx8(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet8.absoluteFill, style] });
450
732
  }
451
733
 
452
734
  // src/ReadyButton.tsx
453
- import { TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
454
- import { StyleSheet as StyleSheet5 } from "react-native";
455
- import { Text as Text3 } from "react-native-paper";
456
- import { jsx as jsx5 } from "react/jsx-runtime";
735
+ import { TouchableRipple as TouchableRipple4 } from "@rific/feedback-press";
736
+ import { StyleSheet as StyleSheet9 } from "react-native";
737
+ import { Text as Text6 } from "react-native-paper";
738
+ import { jsx as jsx9 } from "react/jsx-runtime";
457
739
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
458
- const readyButtonStyle = [styles4.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
740
+ const readyButtonStyle = [styles8.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
459
741
  const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
460
742
  const labelColorStyle = { color: ready ? "#000000" : color };
461
- return /* @__PURE__ */ jsx5(TouchableRipple3, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx5(Text3, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
743
+ return /* @__PURE__ */ jsx9(TouchableRipple4, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx9(Text6, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
462
744
  }
463
- var styles4 = StyleSheet5.create({
745
+ var styles8 = StyleSheet9.create({
464
746
  readyButton: {
465
747
  alignItems: "center",
466
748
  borderRadius: 12,
@@ -474,20 +756,20 @@ var styles4 = StyleSheet5.create({
474
756
 
475
757
  // src/SectionedDropdown.tsx
476
758
  import { getBlendedColor, getColorRoles } from "@rific/auto-paper";
477
- import { IconButton, TouchableRipple as TouchableRipple4 } from "@rific/feedback-press";
478
- import { ScrollView as ScrollView3, StyleSheet as StyleSheet6, View as View5 } from "react-native";
479
- import { Icon as Icon3, Text as Text4 } from "react-native-paper";
759
+ import { IconButton as IconButton3, TouchableRipple as TouchableRipple5 } from "@rific/feedback-press";
760
+ import { ScrollView as ScrollView5, StyleSheet as StyleSheet10, View as View8 } from "react-native";
761
+ import { Icon as Icon6, Text as Text7 } from "react-native-paper";
480
762
 
481
763
  // src/TriggerGaugeHost.tsx
482
764
  import { lazy, Suspense } from "react";
483
- import { jsx as jsx6 } from "react/jsx-runtime";
765
+ import { jsx as jsx10 } from "react/jsx-runtime";
484
766
  var LazyTriggerGauge = lazy(() => loadSkiaWeb().then(() => import("./TriggerGauge-2OJOOF6I.mjs").then((m) => ({ default: m.TriggerGauge }))));
485
767
  function TriggerGaugeHost(props) {
486
- return /* @__PURE__ */ jsx6(Suspense, { fallback: null, children: /* @__PURE__ */ jsx6(LazyTriggerGauge, { ...props }) });
768
+ return /* @__PURE__ */ jsx10(Suspense, { fallback: null, children: /* @__PURE__ */ jsx10(LazyTriggerGauge, { ...props }) });
487
769
  }
488
770
 
489
771
  // src/SectionedDropdown.tsx
490
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
772
+ import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
491
773
  var MENU_BORDER_WIDTH = 1;
492
774
  var TRIGGER_SIZE = 44;
493
775
  var MENU_MIN_WIDTH = 200;
@@ -533,17 +815,17 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
533
815
  }
534
816
  gaugeOffset += section.options.length;
535
817
  }
536
- const anchorStyle = [styles5.anchor, open && styles5.anchorOpen];
818
+ const anchorStyle = [styles9.anchor, open && styles9.anchorOpen];
537
819
  const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
538
820
  const dividerStyle = { backgroundColor: menuBorderColor };
539
- return /* @__PURE__ */ jsxs4(View5, { style: anchorStyle, children: [
540
- /* @__PURE__ */ jsxs4(View5, { ref: triggerRef, collapsable: false, style: styles5.triggerBox, children: [
541
- /* @__PURE__ */ jsx7(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
542
- /* @__PURE__ */ jsx7(IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
821
+ return /* @__PURE__ */ jsxs8(View8, { style: anchorStyle, children: [
822
+ /* @__PURE__ */ jsxs8(View8, { ref: triggerRef, collapsable: false, style: styles9.triggerBox, children: [
823
+ /* @__PURE__ */ jsx11(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
824
+ /* @__PURE__ */ jsx11(IconButton3, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
543
825
  ] }),
544
- /* @__PURE__ */ jsx7(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx7(ScrollView3, { style: [styles5.menu, menuStyle], contentContainerStyle: styles5.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs4(View5, { children: [
545
- sectionIndex > 0 && /* @__PURE__ */ jsx7(View5, { style: [styles5.divider, dividerStyle] }),
546
- section.kind === "single" ? /* @__PURE__ */ jsx7(
826
+ /* @__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: [
827
+ sectionIndex > 0 && /* @__PURE__ */ jsx11(View8, { style: [styles9.divider, dividerStyle] }),
828
+ section.kind === "single" ? /* @__PURE__ */ jsx11(
547
829
  SingleSection,
548
830
  {
549
831
  section,
@@ -556,7 +838,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
556
838
  if (autoDismiss) host.close();
557
839
  }
558
840
  }
559
- ) : /* @__PURE__ */ jsx7(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
841
+ ) : /* @__PURE__ */ jsx11(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
560
842
  ] }, section.id)) }) })
561
843
  ] });
562
844
  }
@@ -568,17 +850,17 @@ function SingleSection({
568
850
  labelFontFamily,
569
851
  onSelect
570
852
  }) {
571
- return /* @__PURE__ */ jsx7(Fragment3, { children: section.options.map((option) => {
853
+ return /* @__PURE__ */ jsx11(Fragment4, { children: section.options.map((option) => {
572
854
  const selected = option.value === section.value;
573
855
  const rowColor = selected ? onAccentColor : mutedColor;
574
- const itemStyle = [styles5.item, selected && { backgroundColor: accentColor }];
856
+ const itemStyle = [styles9.item, selected && { backgroundColor: accentColor }];
575
857
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
576
858
  const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
577
- return /* @__PURE__ */ jsx7(TouchableRipple4, { onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs4(View5, { style: styles5.itemRow, children: [
578
- option.icon && /* @__PURE__ */ jsx7(Icon3, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
579
- /* @__PURE__ */ jsxs4(View5, { style: styles5.itemLabelColumn, children: [
580
- /* @__PURE__ */ jsx7(Text4, { variant: "labelLarge", style: labelStyle, children: option.label }),
581
- option.description && /* @__PURE__ */ jsx7(Text4, { variant: "labelSmall", style: descriptionStyle, children: option.description })
859
+ return /* @__PURE__ */ jsx11(TouchableRipple5, { onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs8(View8, { style: styles9.itemRow, children: [
860
+ option.icon && /* @__PURE__ */ jsx11(Icon6, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
861
+ /* @__PURE__ */ jsxs8(View8, { style: styles9.itemLabelColumn, children: [
862
+ /* @__PURE__ */ jsx11(Text7, { variant: "labelLarge", style: labelStyle, children: option.label }),
863
+ option.description && /* @__PURE__ */ jsx11(Text7, { variant: "labelSmall", style: descriptionStyle, children: option.description })
582
864
  ] })
583
865
  ] }) }, option.value);
584
866
  }) });
@@ -592,23 +874,23 @@ function MultiSection({
592
874
  allClearLabels
593
875
  }) {
594
876
  const allSelected = section.value.length === section.options.length;
595
- return /* @__PURE__ */ jsxs4(Fragment3, { children: [
877
+ return /* @__PURE__ */ jsxs8(Fragment4, { children: [
596
878
  section.options.map((option, i) => {
597
879
  const selected = section.value.includes(option.value);
598
880
  const rowColor = selected ? onAccentColor : mutedColor;
599
881
  const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
600
882
  const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
601
- const itemStyle = [styles5.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
883
+ const itemStyle = [styles9.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
602
884
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
603
- return /* @__PURE__ */ jsx7(TouchableRipple4, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs4(View5, { style: styles5.itemRow, children: [
604
- option.icon && /* @__PURE__ */ jsx7(Icon3, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
605
- /* @__PURE__ */ jsx7(Text4, { variant: "labelLarge", style: [styles5.itemLabelFlex, labelStyle], children: option.label })
885
+ 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: [
886
+ option.icon && /* @__PURE__ */ jsx11(Icon6, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
887
+ /* @__PURE__ */ jsx11(Text7, { variant: "labelLarge", style: [styles9.itemLabelFlex, labelStyle], children: option.label })
606
888
  ] }) }, option.value);
607
889
  }),
608
- section.allClear && /* @__PURE__ */ jsx7(TouchableRipple4, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles5.item, children: /* @__PURE__ */ jsx7(Text4, { variant: "labelLarge", style: [styles5.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
890
+ 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 }) })
609
891
  ] });
610
892
  }
611
- var styles5 = StyleSheet6.create({
893
+ var styles9 = StyleSheet10.create({
612
894
  allClearLabel: {
613
895
  fontWeight: "bold",
614
896
  textAlign: "center"
@@ -625,7 +907,7 @@ var styles5 = StyleSheet6.create({
625
907
  zIndex: 100
626
908
  },
627
909
  divider: {
628
- height: StyleSheet6.hairlineWidth,
910
+ height: StyleSheet10.hairlineWidth,
629
911
  marginVertical: 4
630
912
  },
631
913
  item: {
@@ -672,10 +954,90 @@ var styles5 = StyleSheet6.create({
672
954
  }
673
955
  });
674
956
 
957
+ // src/SharedActionBand.tsx
958
+ import { IconButton as IconButton4 } from "@rific/feedback-press";
959
+ import { StyleSheet as StyleSheet11, View as View9 } from "react-native";
960
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
961
+ function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
962
+ return /* @__PURE__ */ jsxs9(View9, { style: [styles10.column, popoverOpen && styles10.columnOpen], children: [
963
+ /* @__PURE__ */ jsxs9(View9, { style: styles10.actionsRow, children: [
964
+ /* @__PURE__ */ jsx12(IconButton4, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
965
+ onRandomize && /* @__PURE__ */ jsx12(IconButton4, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
966
+ onReset && /* @__PURE__ */ jsx12(IconButton4, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
967
+ /* @__PURE__ */ jsx12(IconButton4, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
968
+ ] }),
969
+ children
970
+ ] });
971
+ }
972
+ var styles10 = StyleSheet11.create({
973
+ actionsRow: {
974
+ alignItems: "center",
975
+ flexDirection: "row",
976
+ gap: 16
977
+ },
978
+ column: {
979
+ alignItems: "center",
980
+ gap: 8
981
+ },
982
+ columnOpen: {
983
+ zIndex: 100
984
+ }
985
+ });
986
+
987
+ // src/StatRow.tsx
988
+ import { useAutoPaperTheme as useAutoPaperTheme4 } from "@rific/auto-paper";
989
+ import { StyleSheet as StyleSheet12, View as View10 } from "react-native";
990
+ import { Text as Text8 } from "react-native-paper";
991
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
992
+ function StatRow({ label, value }) {
993
+ const { dark } = useAutoPaperTheme4();
994
+ const fg = dark ? "#FFFFFF" : "#000000";
995
+ const fgMuted = dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)";
996
+ return /* @__PURE__ */ jsxs10(View10, { style: styles11.row, children: [
997
+ /* @__PURE__ */ jsx13(Text8, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
998
+ /* @__PURE__ */ jsx13(Text8, { variant: "bodyMedium", style: [styles11.value, { color: fg }], children: value })
999
+ ] });
1000
+ }
1001
+ var styles11 = StyleSheet12.create({
1002
+ row: {
1003
+ alignItems: "center",
1004
+ flexDirection: "row",
1005
+ justifyContent: "space-between"
1006
+ },
1007
+ value: {
1008
+ fontWeight: "bold"
1009
+ }
1010
+ });
1011
+
1012
+ // src/StatSection.tsx
1013
+ import { useAutoPaperTheme as useAutoPaperTheme5 } from "@rific/auto-paper";
1014
+ import { StyleSheet as StyleSheet13, View as View11 } from "react-native";
1015
+ import { Text as Text9 } from "react-native-paper";
1016
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1017
+ function StatSection({ label, children }) {
1018
+ const { dark } = useAutoPaperTheme5();
1019
+ const fgMuted = dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)";
1020
+ const sectionBg = dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)";
1021
+ return /* @__PURE__ */ jsxs11(View11, { style: [styles12.section, { backgroundColor: sectionBg }], children: [
1022
+ /* @__PURE__ */ jsx14(Text9, { variant: "labelMedium", style: [styles12.label, { color: fgMuted }], children: label }),
1023
+ children
1024
+ ] });
1025
+ }
1026
+ var styles12 = StyleSheet13.create({
1027
+ label: {
1028
+ letterSpacing: 2
1029
+ },
1030
+ section: {
1031
+ borderRadius: 12,
1032
+ gap: 8,
1033
+ padding: 16
1034
+ }
1035
+ });
1036
+
675
1037
  // src/usePopoverHost.ts
676
- import { useCallback as useCallback2, useState as useState3 } from "react";
1038
+ import { useCallback as useCallback2, useState as useState4 } from "react";
677
1039
  function usePopoverHost() {
678
- const [openId, setOpenId] = useState3(null);
1040
+ const [openId, setOpenId] = useState4(null);
679
1041
  const toggle = useCallback2((id) => {
680
1042
  setOpenId((prev) => prev === id ? null : id);
681
1043
  }, []);
@@ -683,14 +1045,24 @@ function usePopoverHost() {
683
1045
  return { openId, toggle, close };
684
1046
  }
685
1047
  export {
1048
+ AchievementRow,
686
1049
  BaseSettingsDialog,
1050
+ BaseStatsScreen,
1051
+ CornerActionButtons,
687
1052
  InlineColorPicker,
1053
+ LABELED_DROPDOWN_POPOVER_WIDTH,
1054
+ LOCKED_BADGE_COLOR,
1055
+ LabeledDropdown,
688
1056
  MONO_FONT,
689
1057
  PopoverBody,
690
1058
  PressAwayOverlay,
691
1059
  ReadyButton,
692
1060
  SectionedDropdown,
1061
+ SharedActionBand,
1062
+ StatRow,
1063
+ StatSection,
693
1064
  TriggerGaugeHost,
1065
+ getLabeledDropdownContentHeight,
694
1066
  loadSkiaWeb,
695
1067
  useAutoAlign,
696
1068
  usePopoverHost