@tastic/hud 0.3.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.js CHANGED
@@ -28,22 +28,22 @@ __export(TriggerGauge_exports, {
28
28
  function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, dashWidth, startDeg = DEFAULT_START_DEG, endDeg = startDeg }) {
29
29
  const totalDurationMs = MOUNT_DURATION_MS + Math.max(0, segments - 1) * MOUNT_STAGGER_MS;
30
30
  const mountProgress = (0, import_react_native_reanimated.useSharedValue)(0);
31
- (0, import_react3.useLayoutEffect)(() => {
31
+ (0, import_react4.useLayoutEffect)(() => {
32
32
  mountProgress.value = (0, import_react_native_reanimated.withTiming)(1, { duration: totalDurationMs, easing: import_react_native_reanimated.Easing.out(import_react_native_reanimated.Easing.cubic) });
33
33
  }, []);
34
34
  const rawSweepDeg = ((endDeg - startDeg) % 360 + 360) % 360;
35
35
  const totalSweepDeg = rawSweepDeg === 0 ? 360 : rawSweepDeg;
36
36
  const step = segments > 0 ? totalSweepDeg / segments : 0;
37
- const centerDeg = (0, import_react3.useCallback)((i) => startDeg + (i + 0.5) * step, [startDeg, step]);
37
+ const centerDeg = (0, import_react4.useCallback)((i) => startDeg + (i + 0.5) * step, [startDeg, step]);
38
38
  const singleSelect = litIndices.length === 1;
39
39
  const angleDeg = (0, import_react_native_reanimated.useSharedValue)(centerDeg(litIndices[0] ?? 0));
40
40
  const litIndicesKey = [...litIndices].sort((a, b) => a - b).join(",");
41
- const [transitionFrom, setTransitionFrom] = (0, import_react3.useState)(litIndices);
41
+ const [transitionFrom, setTransitionFrom] = (0, import_react4.useState)(litIndices);
42
42
  const transitionFromKey = [...transitionFrom].sort((a, b) => a - b).join(",");
43
- const lastKeyRef = (0, import_react3.useRef)(litIndicesKey);
44
- const lastIndicesRef = (0, import_react3.useRef)(litIndices);
43
+ const lastKeyRef = (0, import_react4.useRef)(litIndicesKey);
44
+ const lastIndicesRef = (0, import_react4.useRef)(litIndices);
45
45
  const selectionProgress = (0, import_react_native_reanimated.useSharedValue)(1);
46
- (0, import_react3.useLayoutEffect)(() => {
46
+ (0, import_react4.useLayoutEffect)(() => {
47
47
  if (litIndicesKey !== lastKeyRef.current) {
48
48
  if (singleSelect && lastIndicesRef.current.length === 1) {
49
49
  angleDeg.value = (0, import_react_native_reanimated.withTiming)(centerDeg(litIndices[0]), { duration: SELECTION_TRANSITION_DURATION_MS, easing: import_react_native_reanimated.Easing.out(import_react_native_reanimated.Easing.cubic) });
@@ -120,21 +120,21 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
120
120
  height: canvasSize,
121
121
  width: canvasSize
122
122
  };
123
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native10.View, { style: [styles7.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
124
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_skia.Path, { path: unlitPath, style: "stroke", strokeWidth: UNLIT_THICKNESS, strokeCap: "round", color: mutedColor, opacity: 0.35 }),
125
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_skia.Path, { path: litPath, style: "stroke", strokeWidth: LIT_THICKNESS, strokeCap: "round", color: accentColor, opacity: 1 })
123
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: [styles9.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
124
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_skia.Path, { path: unlitPath, style: "stroke", strokeWidth: UNLIT_THICKNESS, strokeCap: "round", color: mutedColor, opacity: 0.35 }),
125
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_skia.Path, { path: litPath, style: "stroke", strokeWidth: LIT_THICKNESS, strokeCap: "round", color: accentColor, opacity: 1 })
126
126
  ] }) });
127
127
  }
128
- var import_react_native_skia, import_core3, import_react3, import_react_native10, import_react_native_reanimated, import_jsx_runtime8, UNLIT_THICKNESS, LIT_THICKNESS, DASH_GAP_PX, ARC_GAP, DEFAULT_START_DEG, MIN_DASH_ANGLE_DEG, MOUNT_DURATION_MS, MOUNT_STAGGER_MS, SELECTION_TRANSITION_DURATION_MS, styles7;
128
+ var import_react_native_skia, import_core3, import_react4, import_react_native12, import_react_native_reanimated, import_jsx_runtime10, UNLIT_THICKNESS, LIT_THICKNESS, DASH_GAP_PX, ARC_GAP, DEFAULT_START_DEG, MIN_DASH_ANGLE_DEG, MOUNT_DURATION_MS, MOUNT_STAGGER_MS, SELECTION_TRANSITION_DURATION_MS, styles9;
129
129
  var init_TriggerGauge = __esm({
130
130
  "src/TriggerGauge.tsx"() {
131
131
  "use strict";
132
132
  import_react_native_skia = require("@shopify/react-native-skia");
133
133
  import_core3 = require("@tastic/core");
134
- import_react3 = require("react");
135
- import_react_native10 = require("react-native");
134
+ import_react4 = require("react");
135
+ import_react_native12 = require("react-native");
136
136
  import_react_native_reanimated = require("react-native-reanimated");
137
- import_jsx_runtime8 = require("react/jsx-runtime");
137
+ import_jsx_runtime10 = require("react/jsx-runtime");
138
138
  UNLIT_THICKNESS = 3;
139
139
  LIT_THICKNESS = 7;
140
140
  DASH_GAP_PX = 9;
@@ -144,7 +144,7 @@ var init_TriggerGauge = __esm({
144
144
  MOUNT_DURATION_MS = 450;
145
145
  MOUNT_STAGGER_MS = 40;
146
146
  SELECTION_TRANSITION_DURATION_MS = 260;
147
- styles7 = import_react_native10.StyleSheet.create({
147
+ styles9 = import_react_native12.StyleSheet.create({
148
148
  container: {
149
149
  alignItems: "center",
150
150
  justifyContent: "center",
@@ -157,10 +157,13 @@ var init_TriggerGauge = __esm({
157
157
  // src/index.ts
158
158
  var index_exports = {};
159
159
  __export(index_exports, {
160
+ AchievementRow: () => AchievementRow,
160
161
  BaseSettingsDialog: () => BaseSettingsDialog,
162
+ BaseStatsScreen: () => BaseStatsScreen,
161
163
  CornerActionButtons: () => CornerActionButtons,
162
164
  InlineColorPicker: () => InlineColorPicker,
163
165
  LABELED_DROPDOWN_POPOVER_WIDTH: () => LABELED_DROPDOWN_POPOVER_WIDTH,
166
+ LOCKED_BADGE_COLOR: () => LOCKED_BADGE_COLOR,
164
167
  LabeledDropdown: () => LabeledDropdown,
165
168
  MONO_FONT: () => MONO_FONT,
166
169
  PopoverBody: () => PopoverBody,
@@ -168,6 +171,8 @@ __export(index_exports, {
168
171
  ReadyButton: () => ReadyButton,
169
172
  SectionedDropdown: () => SectionedDropdown,
170
173
  SharedActionBand: () => SharedActionBand,
174
+ StatRow: () => StatRow,
175
+ StatSection: () => StatSection,
171
176
  TriggerGaugeHost: () => TriggerGaugeHost,
172
177
  getLabeledDropdownContentHeight: () => getLabeledDropdownContentHeight,
173
178
  loadSkiaWeb: () => loadSkiaWeb,
@@ -176,20 +181,88 @@ __export(index_exports, {
176
181
  });
177
182
  module.exports = __toCommonJS(index_exports);
178
183
 
179
- // src/BaseSettingsDialog.tsx
184
+ // src/AchievementRow.tsx
180
185
  var import_auto_paper = require("@rific/auto-paper");
186
+ var import_react_native = require("react-native");
187
+ var import_react_native_paper = require("react-native-paper");
188
+ var import_jsx_runtime = require("react/jsx-runtime");
189
+ var LOCKED_BADGE_COLOR = "rgba(128,128,128,0.3)";
190
+ function AchievementRow({ icon, title, description, badgeColor, unlockedLabel, checkColor, progress, deviceMarker }) {
191
+ const { dark } = (0, import_auto_paper.useAutoPaperTheme)();
192
+ const fg = dark ? "#FFFFFF" : "#000000";
193
+ const fgMuted = dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)";
194
+ const sectionBg = dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)";
195
+ const unlocked = unlockedLabel !== void 0;
196
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: [styles.row, { backgroundColor: sectionBg }], children: [
197
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: [styles.badge, { backgroundColor: badgeColor }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Icon, { source: icon, size: 20, color: unlocked ? "#000000" : fgMuted }) }),
198
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.text, children: [
199
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "bodyLarge", style: [styles.boldText, { color: fg }], children: title }),
200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "bodySmall", style: { color: fgMuted }, children: description }),
201
+ deviceMarker && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.deviceMarker, children: [
202
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Icon, { source: "earth", size: 11, color: fgMuted }),
203
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "labelSmall", style: { color: fgMuted }, children: "Same for every profile" })
204
+ ] }),
205
+ !unlocked && progress !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.ProgressBar, { progress, color: checkColor ?? badgeColor, style: styles.progressBar })
206
+ ] }),
207
+ unlocked ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.status, children: [
208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Icon, { source: "check-circle", size: 20, color: checkColor ?? badgeColor }),
209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "labelSmall", style: { color: fgMuted }, children: unlockedLabel })
210
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Icon, { source: "lock-outline", size: 20, color: fgMuted })
211
+ ] });
212
+ }
213
+ var styles = import_react_native.StyleSheet.create({
214
+ badge: {
215
+ alignItems: "center",
216
+ borderRadius: 10,
217
+ height: 36,
218
+ justifyContent: "center",
219
+ width: 36
220
+ },
221
+ boldText: {
222
+ fontWeight: "bold"
223
+ },
224
+ deviceMarker: {
225
+ alignItems: "center",
226
+ flexDirection: "row",
227
+ gap: 4
228
+ },
229
+ progressBar: {
230
+ borderRadius: 4,
231
+ height: 6,
232
+ marginTop: 6
233
+ },
234
+ row: {
235
+ alignItems: "center",
236
+ borderRadius: 12,
237
+ flexDirection: "row",
238
+ gap: 12,
239
+ padding: 12
240
+ },
241
+ status: {
242
+ alignItems: "center",
243
+ gap: 2,
244
+ width: 64
245
+ },
246
+ text: {
247
+ flex: 1,
248
+ gap: 2
249
+ }
250
+ });
251
+
252
+ // src/BaseSettingsDialog.tsx
253
+ var import_auto_paper2 = require("@rific/auto-paper");
181
254
  var import_feedback_press = require("@rific/feedback-press");
182
255
  var import_updater = require("@rific/updater");
183
256
  var import_core = require("@tastic/core");
184
257
  var import_react = require("react");
185
- var import_react_native = require("react-native");
186
- var import_react_native_paper = require("react-native-paper");
187
- var import_jsx_runtime = require("react/jsx-runtime");
258
+ var import_react_native2 = require("react-native");
259
+ var import_react_native_paper2 = require("react-native-paper");
260
+ var import_jsx_runtime2 = require("react/jsx-runtime");
188
261
  function SettingIcon({ source, color, containerColor }) {
189
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: [styles.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Icon, { source, size: 18, color }) });
262
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [styles2.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Icon, { source, size: 18, color }) });
190
263
  }
191
264
  function BaseSettingsDialog({ visible, onDismiss, rotation = 0, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, children }) {
192
- const { dark, colors } = (0, import_auto_paper.useAutoPaperTheme)();
265
+ const { dark, colors } = (0, import_auto_paper2.useAutoPaperTheme)();
193
266
  const isTouchPrimary = (0, import_core.useIsTouchPrimaryDevice)();
194
267
  const { settings: hapticSettings, set: setHapticSettings } = (0, import_feedback_press.useHapticSettings)();
195
268
  const { settings: soundSettings, set: setSoundSettings } = (0, import_feedback_press.useSoundSettings)();
@@ -203,35 +276,35 @@ function BaseSettingsDialog({ visible, onDismiss, rotation = 0, version, lockOri
203
276
  onInfo: (title, message) => setInfoMessage({ title, message })
204
277
  });
205
278
  const showLockOrientation = isTouchPrimary && lockOrientation !== void 0 && onLockOrientationChange !== void 0;
206
- const showEdgeGuard = import_react_native.Platform.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
207
- const showHaptics = import_react_native.Platform.OS !== "web" && !hideHaptics;
279
+ const showEdgeGuard = import_react_native2.Platform.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
280
+ const showHaptics = import_react_native2.Platform.OS !== "web" && !hideHaptics;
208
281
  const showSound = !hideSound;
209
282
  const showAppearance = !hideAppearance;
210
- const showUpdateCheck = import_react_native.Platform.OS !== "web" && !hideUpdateCheck;
283
+ const showUpdateCheck = import_react_native2.Platform.OS !== "web" && !hideUpdateCheck;
211
284
  const fg = dark ? "#FFFFFF" : "#000000";
212
285
  const cardBg = dark ? "#111111" : "#F2F2F2";
213
286
  const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
214
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
215
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_auto_paper.Dialog, { visible, onDismiss, style: [styles.dialog, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
216
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_auto_paper.Dialog.Title, { children: "Settings" }),
217
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_auto_paper.Dialog.ScrollArea, { style: styles.scrollArea, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.ScrollView, { contentContainerStyle: styles.content, showsVerticalScrollIndicator: false, children: [
218
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.toggleGroup, children: [
219
- showLockOrientation && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_feedback_press.TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.toggleContent, children: [
220
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
221
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.flexShrink, children: [
222
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
223
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
287
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
288
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_auto_paper2.Dialog, { visible, onDismiss, style: [styles2.dialog, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
289
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_auto_paper2.Dialog.Title, { children: "Settings" }),
290
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_auto_paper2.Dialog.ScrollArea, { style: styles2.scrollArea, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.ScrollView, { contentContainerStyle: styles2.content, showsVerticalScrollIndicator: false, children: [
291
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.toggleGroup, children: [
292
+ showLockOrientation && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_feedback_press.TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles2.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.toggleContent, children: [
293
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
294
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.flexShrink, children: [
295
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
296
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
224
297
  ] })
225
298
  ] }) }),
226
- showEdgeGuard && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_feedback_press.TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.toggleContent, children: [
227
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
228
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.flexShrink, children: [
229
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
230
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
299
+ showEdgeGuard && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_feedback_press.TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles2.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.toggleContent, children: [
300
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
301
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.flexShrink, children: [
302
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
303
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
231
304
  ] })
232
305
  ] }) }),
233
- (showSound || showHaptics) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.toggleRow, children: [
234
- showSound && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
306
+ (showSound || showHaptics) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.toggleRow, children: [
307
+ showSound && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
235
308
  import_feedback_press.TouchableRipple,
236
309
  {
237
310
  soundDisabled: true,
@@ -240,15 +313,15 @@ function BaseSettingsDialog({ visible, onDismiss, rotation = 0, version, lockOri
240
313
  setSoundSettings({ enabled });
241
314
  if (enabled) sound.selection?.();
242
315
  },
243
- style: styles.toggleButtonInRow,
316
+ style: styles2.toggleButtonInRow,
244
317
  accessibilityLabel: `Sound ${soundSettings.enabled ? "on" : "off"}`,
245
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.toggleContent, children: [
246
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
247
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
318
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.toggleContent, children: [
319
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
320
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
248
321
  ] })
249
322
  }
250
323
  ),
251
- showHaptics && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
324
+ showHaptics && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
252
325
  import_feedback_press.TouchableRipple,
253
326
  {
254
327
  hapticDisabled: true,
@@ -257,40 +330,40 @@ function BaseSettingsDialog({ visible, onDismiss, rotation = 0, version, lockOri
257
330
  setHapticSettings({ vibrate });
258
331
  if (vibrate) forceHapticFeedback();
259
332
  },
260
- style: styles.toggleButtonInRow,
333
+ style: styles2.toggleButtonInRow,
261
334
  accessibilityLabel: `Haptics ${hapticSettings.vibrate ? "on" : "off"}`,
262
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.toggleContent, children: [
263
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
264
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
335
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.toggleContent, children: [
336
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
337
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
265
338
  ] })
266
339
  }
267
340
  )
268
341
  ] })
269
342
  ] }),
270
- showAppearance && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.section, children: [
271
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "labelMedium", style: [styles.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
272
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_auto_paper.AutoAppearancePicker, { showLabels: false })
343
+ showAppearance && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.section, children: [
344
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "labelMedium", style: [styles2.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
345
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_auto_paper2.AutoAppearancePicker, { showLabels: false })
273
346
  ] }),
274
347
  children,
275
- showUpdateCheck && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.section, children: [
276
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native_paper.Text, { variant: "labelSmall", style: [styles.sectionLabel, { color: colors.onSurfaceVariant }], children: [
348
+ showUpdateCheck && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.section, children: [
349
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_paper2.Text, { variant: "labelSmall", style: [styles2.sectionLabel, { color: colors.onSurfaceVariant }], children: [
277
350
  "VERSION ",
278
351
  version,
279
352
  updateReady ? " \xB7 UPDATE READY" : ""
280
353
  ] }),
281
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_feedback_press.Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
354
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_feedback_press.Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
282
355
  ] })
283
356
  ] }) })
284
357
  ] }),
285
- infoMessage && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: styles.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: [styles.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
286
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Icon, { source: "information-outline", size: 64, color: colors.secondary }),
287
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "headlineLarge", style: [styles.overlayTitle, { color: colors.secondary }], children: infoMessage.title }),
288
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { variant: "bodyLarge", style: [styles.overlayBody, { color: fg }], children: infoMessage.message }),
289
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_feedback_press.Button, { mode: "contained", onPress: () => setInfoMessage(null), style: styles.overlayButton, buttonColor: colors.primary, textColor: colors.onPrimary, children: "OK" })
358
+ infoMessage && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles2.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: [styles2.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
359
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Icon, { source: "information-outline", size: 64, color: colors.secondary }),
360
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "headlineLarge", style: [styles2.overlayTitle, { color: colors.secondary }], children: infoMessage.title }),
361
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodyLarge", style: [styles2.overlayBody, { color: fg }], children: infoMessage.message }),
362
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_feedback_press.Button, { mode: "contained", onPress: () => setInfoMessage(null), style: styles2.overlayButton, buttonColor: colors.primary, textColor: colors.onPrimary, children: "OK" })
290
363
  ] }) }) })
291
364
  ] });
292
365
  }
293
- var styles = import_react_native.StyleSheet.create({
366
+ var styles2 = import_react_native2.StyleSheet.create({
294
367
  content: {
295
368
  gap: 24,
296
369
  paddingVertical: 20
@@ -378,17 +451,117 @@ var styles = import_react_native.StyleSheet.create({
378
451
  }
379
452
  });
380
453
 
381
- // src/CornerActionButtons.tsx
454
+ // src/BaseStatsScreen.tsx
455
+ var import_auto_paper3 = require("@rific/auto-paper");
382
456
  var import_feedback_press2 = require("@rific/feedback-press");
383
- var import_react_native2 = require("react-native");
384
- var import_jsx_runtime2 = require("react/jsx-runtime");
457
+ var import_react2 = require("react");
458
+ var import_react_native3 = require("react-native");
459
+ var import_react_native_paper3 = require("react-native-paper");
460
+ var import_jsx_runtime3 = require("react/jsx-runtime");
461
+ 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 }) {
462
+ const { dark, colors } = (0, import_auto_paper3.useAutoPaperTheme)();
463
+ const [confirmResetVisible, setConfirmResetVisible] = (0, import_react2.useState)(false);
464
+ const showReset = !!onReset;
465
+ const fg = dark ? "#FFFFFF" : "#000000";
466
+ const bg = dark ? "#000000" : "#FFFFFF";
467
+ const cardBg = dark ? "#111111" : "#F2F2F2";
468
+ const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
469
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [styles3.container, { backgroundColor: bg }], children: [
470
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [styles3.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
471
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
472
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "displaySmall", style: [styles3.title, { color: fg }], children: title })
473
+ ] }),
474
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.ScrollView, { style: styles3.scrollView, contentContainerStyle: [styles3.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
475
+ children,
476
+ showReset && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.Button, { mode: "outlined", onPress: () => setConfirmResetVisible(true), textColor: colors.secondary, style: styles3.resetButton, children: resetLabel })
477
+ ] }),
478
+ confirmResetVisible && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [styles3.overlay, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [styles3.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }], children: [
479
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Icon, { source: "alert-outline", size: 64, color: colors.secondary }),
480
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "headlineLarge", style: [styles3.overlayTitle, { color: colors.secondary }], children: resetConfirmTitle }),
481
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "bodyLarge", style: [styles3.overlayBody, { color: fg }], children: resetConfirmBody }),
482
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.Button, { mode: "contained", onPress: () => setConfirmResetVisible(false), style: styles3.overlayButton, children: "Cancel" }),
483
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
484
+ import_feedback_press2.Button,
485
+ {
486
+ mode: "contained",
487
+ onPress: () => {
488
+ onReset?.();
489
+ setConfirmResetVisible(false);
490
+ },
491
+ style: styles3.overlayButton,
492
+ buttonColor: colors.secondary,
493
+ textColor: colors.onSecondary,
494
+ children: "Reset"
495
+ }
496
+ )
497
+ ] }) }) })
498
+ ] });
499
+ }
500
+ var styles3 = import_react_native3.StyleSheet.create({
501
+ container: {
502
+ flex: 1
503
+ },
504
+ content: {
505
+ gap: 12,
506
+ paddingHorizontal: 20
507
+ },
508
+ header: {
509
+ alignItems: "center",
510
+ flexDirection: "row",
511
+ gap: 4,
512
+ paddingBottom: 8
513
+ },
514
+ // Same overlay shape BaseSettingsDialog's own info overlay uses (backdrop + centered card),
515
+ // rotated in place the same way rather than via Portal-root transform for the same reason — see
516
+ // that component's own identical styles for the full rationale.
517
+ overlay: {
518
+ alignItems: "center",
519
+ backgroundColor: "rgba(0,0,0,0.72)",
520
+ bottom: 0,
521
+ justifyContent: "center",
522
+ left: 0,
523
+ position: "absolute",
524
+ right: 0,
525
+ top: 0
526
+ },
527
+ overlayBody: { textAlign: "center" },
528
+ overlayButton: { width: 160 },
529
+ overlayCard: {
530
+ alignItems: "center",
531
+ borderRadius: 20,
532
+ borderWidth: 1,
533
+ gap: 16,
534
+ maxWidth: 360,
535
+ padding: 32
536
+ },
537
+ overlayTitle: { fontWeight: "bold", marginBottom: -8 },
538
+ resetButton: {
539
+ marginTop: 16
540
+ },
541
+ // Bounds the ScrollView to the space `container`'s flex:1 actually gives it — without this, a
542
+ // ScrollView with only a contentContainerStyle isn't reliably height-constrained on native (it
543
+ // can render at its full unclipped content height instead of the screen's), which leaves the
544
+ // header's back button touch target unreliable underneath it.
545
+ scrollView: {
546
+ flex: 1
547
+ },
548
+ title: {
549
+ flexShrink: 1,
550
+ fontWeight: "bold"
551
+ }
552
+ });
553
+
554
+ // src/CornerActionButtons.tsx
555
+ var import_feedback_press3 = require("@rific/feedback-press");
556
+ var import_react_native4 = require("react-native");
557
+ var import_jsx_runtime4 = require("react/jsx-runtime");
385
558
  function CornerActionButtons({ onBack, onSettings, fg, insets }) {
386
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
387
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_feedback_press2.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, style: [styles2.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: "Back" }),
388
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_feedback_press2.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles2.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
559
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
560
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, style: [styles4.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: "Back" }),
561
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press3.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles4.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
389
562
  ] });
390
563
  }
391
- var styles2 = import_react_native2.StyleSheet.create({
564
+ var styles4 = import_react_native4.StyleSheet.create({
392
565
  back: {
393
566
  position: "absolute"
394
567
  },
@@ -398,41 +571,41 @@ var styles2 = import_react_native2.StyleSheet.create({
398
571
  });
399
572
 
400
573
  // src/fonts.ts
401
- var import_react_native3 = require("react-native");
402
- var MONO_FONT = import_react_native3.Platform.select({ ios: "Menlo", android: "monospace", default: "monospace" });
574
+ var import_react_native5 = require("react-native");
575
+ var MONO_FONT = import_react_native5.Platform.select({ ios: "Menlo", android: "monospace", default: "monospace" });
403
576
 
404
577
  // src/InlineColorPicker.tsx
405
- var import_auto_paper2 = require("@rific/auto-paper");
406
- var import_feedback_press3 = require("@rific/feedback-press");
578
+ var import_auto_paper4 = require("@rific/auto-paper");
579
+ var import_feedback_press4 = require("@rific/feedback-press");
407
580
  var import_core2 = require("@tastic/core");
408
- var import_react_native6 = require("react-native");
409
- var import_react_native_paper2 = require("react-native-paper");
581
+ var import_react_native8 = require("react-native");
582
+ var import_react_native_paper4 = require("react-native-paper");
410
583
 
411
584
  // src/PopoverBody.tsx
412
- var import_react_native4 = require("react-native");
413
- var import_jsx_runtime3 = require("react/jsx-runtime");
585
+ var import_react_native6 = require("react-native");
586
+ var import_jsx_runtime5 = require("react/jsx-runtime");
414
587
  var CARET_SIZE = 7;
415
588
  var CARET_RING_OFFSET = 2;
416
589
  var CARET_DIP = 1;
417
590
  function PopoverBody({ visible, children, align = "center", verticalAlign = "below", caretColor, caretBorderColor, caretBorderWidth = 1, triggerSize = 0 }) {
418
591
  if (!visible) return null;
419
592
  const above = verticalAlign === "above";
420
- const alignStyle = align === "left" ? styles3.contentLeft : align === "right" ? styles3.contentRight : styles3.contentCenter;
421
- const verticalStyle = above ? styles3.contentAbove : styles3.contentBelow;
593
+ const alignStyle = align === "left" ? styles5.contentLeft : align === "right" ? styles5.contentRight : styles5.contentCenter;
594
+ const verticalStyle = above ? styles5.contentAbove : styles5.contentBelow;
422
595
  const ringSize = CARET_SIZE + caretBorderWidth;
423
596
  const caretOffset = (halfFootprint) => align === "right" ? { right: triggerSize / 2 - halfFootprint } : { left: triggerSize / 2 - halfFootprint };
424
597
  const caretFill = above ? "borderTopColor" : "borderBottomColor";
425
598
  const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
426
599
  const caretEdge = (size) => above ? { bottom: -size + CARET_DIP } : { top: -size + CARET_DIP };
427
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: [styles3.content, alignStyle, verticalStyle], children: [
600
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native6.View, { style: [styles5.content, alignStyle, verticalStyle], children: [
428
601
  children,
429
- caretColor && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
430
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native4.View, { style: [styles3.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
431
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native4.View, { style: [styles3.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
602
+ caretColor && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
603
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native6.View, { style: [styles5.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
604
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native6.View, { style: [styles5.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
432
605
  ] })
433
606
  ] });
434
607
  }
435
- var styles3 = import_react_native4.StyleSheet.create({
608
+ var styles5 = import_react_native6.StyleSheet.create({
436
609
  caret: {
437
610
  borderLeftColor: "transparent",
438
611
  borderRightColor: "transparent",
@@ -475,22 +648,22 @@ var styles3 = import_react_native4.StyleSheet.create({
475
648
  });
476
649
 
477
650
  // src/useAutoAlign.ts
478
- var import_react2 = require("react");
479
- var import_react_native5 = require("react-native");
651
+ var import_react3 = require("react");
652
+ var import_react_native7 = require("react-native");
480
653
  var EDGE_MARGIN = 12;
481
654
  function useAutoAlign(open, contentWidth, contentHeight) {
482
- const triggerRef = (0, import_react2.useRef)(null);
483
- const { width: windowWidth, height: windowHeight } = (0, import_react_native5.useWindowDimensions)();
484
- const [align, setAlign] = (0, import_react2.useState)("center");
485
- const [verticalAlign, setVerticalAlign] = (0, import_react2.useState)("below");
486
- const [maxHeight, setMaxHeight] = (0, import_react2.useState)(contentHeight);
487
- const [measured, setMeasured] = (0, import_react2.useState)(false);
488
- const [prevOpen, setPrevOpen] = (0, import_react2.useState)(open);
655
+ const triggerRef = (0, import_react3.useRef)(null);
656
+ const { width: windowWidth, height: windowHeight } = (0, import_react_native7.useWindowDimensions)();
657
+ const [align, setAlign] = (0, import_react3.useState)("center");
658
+ const [verticalAlign, setVerticalAlign] = (0, import_react3.useState)("below");
659
+ const [maxHeight, setMaxHeight] = (0, import_react3.useState)(contentHeight);
660
+ const [measured, setMeasured] = (0, import_react3.useState)(false);
661
+ const [prevOpen, setPrevOpen] = (0, import_react3.useState)(open);
489
662
  if (open !== prevOpen) {
490
663
  setPrevOpen(open);
491
664
  if (open) setMeasured(false);
492
665
  }
493
- const measure = (0, import_react2.useCallback)(() => {
666
+ const measure = (0, import_react3.useCallback)(() => {
494
667
  triggerRef.current?.measureInWindow((x, y, triggerWidth, triggerHeight) => {
495
668
  const centerX = x + triggerWidth / 2;
496
669
  const overflowsRight = centerX + contentWidth / 2 > windowWidth - EDGE_MARGIN;
@@ -506,14 +679,14 @@ function useAutoAlign(open, contentWidth, contentHeight) {
506
679
  setMeasured(true);
507
680
  });
508
681
  }, [contentWidth, contentHeight, windowWidth, windowHeight]);
509
- (0, import_react2.useEffect)(() => {
682
+ (0, import_react3.useEffect)(() => {
510
683
  if (open) measure();
511
684
  }, [open, measure]);
512
685
  return { align, maxHeight, measured, triggerRef, verticalAlign };
513
686
  }
514
687
 
515
688
  // src/InlineColorPicker.tsx
516
- var import_jsx_runtime4 = require("react/jsx-runtime");
689
+ var import_jsx_runtime6 = require("react/jsx-runtime");
517
690
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
518
691
  var DEFAULT_SIZE = 48;
519
692
  var SWATCH_SIZE = 28;
@@ -526,9 +699,9 @@ var SCREEN_MARGIN = 40;
526
699
  function widthForColumns(columns) {
527
700
  return SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * columns + SWATCHES_GAP * (columns - 1);
528
701
  }
529
- function InlineColorPicker({ id, host, value, onChange, swatches = import_auto_paper2.defaultColors, takenValue, allowSwapTaken, dark, align: alignOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
702
+ function InlineColorPicker({ id, host, value, onChange, swatches = import_auto_paper4.defaultColors, takenValue, allowSwapTaken, dark, align: alignOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
530
703
  const menuBg = dark ? "#000000" : "#FFFFFF";
531
- const { width: windowWidth } = (0, import_react_native6.useWindowDimensions)();
704
+ const { width: windowWidth } = (0, import_react_native8.useWindowDimensions)();
532
705
  const autoColumns = (0, import_core2.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
533
706
  const resolvedColumns = columns ?? autoColumns;
534
707
  const swatchesWidth = widthForColumns(resolvedColumns);
@@ -537,16 +710,16 @@ function InlineColorPicker({ id, host, value, onChange, swatches = import_auto_p
537
710
  const open = host.openId === id;
538
711
  const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, swatchesWidth, swatchesHeight);
539
712
  const align = alignOverride ?? autoAlign;
540
- const contrastColor = (0, import_auto_paper2.getContrastColor)(value);
713
+ const contrastColor = (0, import_auto_paper4.getContrastColor)(value);
541
714
  const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
542
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native6.View, { style: [styles4.anchor, open && styles4.anchorOpen], children: [
543
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press3.TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles4.trigger, { backgroundColor: value, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.View, { ref: triggerRef, collapsable: false, style: styles4.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles4.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
544
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: value, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.ScrollView, { style: [styles4.swatches, { backgroundColor: menuBg, borderColor: value, width: swatchesWidth, maxHeight }], contentContainerStyle: styles4.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
715
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native8.View, { style: [styles6.anchor, open && styles6.anchorOpen], children: [
716
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles6.trigger, { backgroundColor: value, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native8.View, { ref: triggerRef, collapsable: false, style: styles6.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper4.Text, { style: [styles6.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper4.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
717
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: value, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native8.ScrollView, { style: [styles6.swatches, { backgroundColor: menuBg, borderColor: value, width: swatchesWidth, maxHeight }], contentContainerStyle: styles6.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
545
718
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
546
719
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
547
720
  const swappable = taken && allowSwapTaken;
548
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
549
- import_feedback_press3.TouchableRipple,
721
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
722
+ import_feedback_press4.TouchableRipple,
550
723
  {
551
724
  disabled: taken && !swappable,
552
725
  onPress: () => {
@@ -554,15 +727,15 @@ function InlineColorPicker({ id, host, value, onChange, swatches = import_auto_p
554
727
  if (autoDismiss) host.close();
555
728
  },
556
729
  borderless: true,
557
- style: [styles4.swatch, { backgroundColor: swatch.value }, selected && styles4.swatchSelected, taken && !swappable && styles4.swatchTaken],
558
- children: selected ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "check", size: 16, color: (0, import_auto_paper2.getContrastColor)(swatch.value) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper2.getContrastColor)(swatch.value) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "close", size: 16, color: (0, import_auto_paper2.getContrastColor)(swatch.value) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.View, {})
730
+ style: [styles6.swatch, { backgroundColor: swatch.value }, selected && styles6.swatchSelected, taken && !swappable && styles6.swatchTaken],
731
+ children: selected ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper4.Icon, { source: "check", size: 16, color: (0, import_auto_paper4.getContrastColor)(swatch.value) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper4.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper4.getContrastColor)(swatch.value) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper4.Icon, { source: "close", size: 16, color: (0, import_auto_paper4.getContrastColor)(swatch.value) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native8.View, {})
559
732
  },
560
733
  swatch.value
561
734
  );
562
735
  }) }) })
563
736
  ] });
564
737
  }
565
- var styles4 = import_react_native6.StyleSheet.create({
738
+ var styles6 = import_react_native8.StyleSheet.create({
566
739
  anchor: {
567
740
  position: "relative"
568
741
  },
@@ -634,11 +807,11 @@ var styles4 = import_react_native6.StyleSheet.create({
634
807
  });
635
808
 
636
809
  // src/LabeledDropdown.tsx
637
- var import_auto_paper3 = require("@rific/auto-paper");
638
- var import_feedback_press4 = require("@rific/feedback-press");
639
- var import_react_native7 = require("react-native");
640
- var import_react_native_paper3 = require("react-native-paper");
641
- var import_jsx_runtime5 = require("react/jsx-runtime");
810
+ var import_auto_paper5 = require("@rific/auto-paper");
811
+ var import_feedback_press5 = require("@rific/feedback-press");
812
+ var import_react_native9 = require("react-native");
813
+ var import_react_native_paper5 = require("react-native-paper");
814
+ var import_jsx_runtime7 = require("react/jsx-runtime");
642
815
  var TRIGGER_HEIGHT = 28;
643
816
  var POPOVER_WIDTH = 180;
644
817
  var ROW_HEIGHT = 36;
@@ -661,22 +834,22 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
661
834
  onChange(option.value);
662
835
  host.close();
663
836
  };
664
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native7.View, { style: [styles5.anchor, open && styles5.anchorOpen], children: [
665
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => host.toggle(id), style: styles5.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native7.View, { ref: triggerRef, collapsable: false, style: styles5.triggerInner, children: [
666
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper3.Text, { style: [styles5.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
667
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper3.Icon, { source: "menu-down", size: 16, color })
837
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native9.View, { style: [styles7.anchor, open && styles7.anchorOpen], children: [
838
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press5.TouchableRipple, { onPress: () => host.toggle(id), style: styles7.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native9.View, { ref: triggerRef, collapsable: false, style: styles7.triggerInner, children: [
839
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper5.Text, { style: [styles7.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
840
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper5.Icon, { source: "menu-down", size: 16, color })
668
841
  ] }) }),
669
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.ScrollView, { style: [styles5.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles5.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
842
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native9.ScrollView, { style: [styles7.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles7.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
670
843
  const isSelected = option.value === value;
671
- const onColor = (0, import_auto_paper3.getContrastColor)(color);
672
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => handleSelect(option), style: [styles5.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native7.View, { style: styles5.rowInner, children: [
673
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper3.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
674
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper3.Text, { style: [styles5.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
844
+ const onColor = (0, import_auto_paper5.getContrastColor)(color);
845
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press5.TouchableRipple, { onPress: () => handleSelect(option), style: [styles7.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native9.View, { style: styles7.rowInner, children: [
846
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper5.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
847
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper5.Text, { style: [styles7.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
675
848
  ] }) }, option.value);
676
849
  }) }) })
677
850
  ] });
678
851
  }
679
- var styles5 = import_react_native7.StyleSheet.create({
852
+ var styles7 = import_react_native9.StyleSheet.create({
680
853
  anchor: {
681
854
  position: "relative"
682
855
  },
@@ -734,25 +907,25 @@ function loadSkiaWeb() {
734
907
  }
735
908
 
736
909
  // src/PressAwayOverlay.tsx
737
- var import_react_native8 = require("react-native");
738
- var import_jsx_runtime6 = require("react/jsx-runtime");
910
+ var import_react_native10 = require("react-native");
911
+ var import_jsx_runtime8 = require("react/jsx-runtime");
739
912
  function PressAwayOverlay({ active, onPress, style }) {
740
913
  if (!active) return null;
741
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native8.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native8.StyleSheet.absoluteFill, style] });
914
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native10.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native10.StyleSheet.absoluteFill, style] });
742
915
  }
743
916
 
744
917
  // src/ReadyButton.tsx
745
- var import_feedback_press5 = require("@rific/feedback-press");
746
- var import_react_native9 = require("react-native");
747
- var import_react_native_paper4 = require("react-native-paper");
748
- var import_jsx_runtime7 = require("react/jsx-runtime");
918
+ var import_feedback_press6 = require("@rific/feedback-press");
919
+ var import_react_native11 = require("react-native");
920
+ var import_react_native_paper6 = require("react-native-paper");
921
+ var import_jsx_runtime9 = require("react/jsx-runtime");
749
922
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
750
- const readyButtonStyle = [styles6.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
923
+ const readyButtonStyle = [styles8.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
751
924
  const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
752
925
  const labelColorStyle = { color: ready ? "#000000" : color };
753
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press5.TouchableRipple, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper4.Text, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
926
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press6.TouchableRipple, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper6.Text, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
754
927
  }
755
- var styles6 = import_react_native9.StyleSheet.create({
928
+ var styles8 = import_react_native11.StyleSheet.create({
756
929
  readyButton: {
757
930
  alignItems: "center",
758
931
  borderRadius: 12,
@@ -765,21 +938,21 @@ var styles6 = import_react_native9.StyleSheet.create({
765
938
  });
766
939
 
767
940
  // src/SectionedDropdown.tsx
768
- var import_auto_paper4 = require("@rific/auto-paper");
769
- var import_feedback_press6 = require("@rific/feedback-press");
770
- var import_react_native11 = require("react-native");
771
- var import_react_native_paper5 = require("react-native-paper");
941
+ var import_auto_paper6 = require("@rific/auto-paper");
942
+ var import_feedback_press7 = require("@rific/feedback-press");
943
+ var import_react_native13 = require("react-native");
944
+ var import_react_native_paper7 = require("react-native-paper");
772
945
 
773
946
  // src/TriggerGaugeHost.tsx
774
- var import_react4 = require("react");
775
- var import_jsx_runtime9 = require("react/jsx-runtime");
776
- var LazyTriggerGauge = (0, import_react4.lazy)(() => loadSkiaWeb().then(() => Promise.resolve().then(() => (init_TriggerGauge(), TriggerGauge_exports)).then((m) => ({ default: m.TriggerGauge }))));
947
+ var import_react5 = require("react");
948
+ var import_jsx_runtime11 = require("react/jsx-runtime");
949
+ var LazyTriggerGauge = (0, import_react5.lazy)(() => loadSkiaWeb().then(() => Promise.resolve().then(() => (init_TriggerGauge(), TriggerGauge_exports)).then((m) => ({ default: m.TriggerGauge }))));
777
950
  function TriggerGaugeHost(props) {
778
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react4.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LazyTriggerGauge, { ...props }) });
951
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react5.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(LazyTriggerGauge, { ...props }) });
779
952
  }
780
953
 
781
954
  // src/SectionedDropdown.tsx
782
- var import_jsx_runtime10 = require("react/jsx-runtime");
955
+ var import_jsx_runtime12 = require("react/jsx-runtime");
783
956
  var MENU_BORDER_WIDTH = 1;
784
957
  var TRIGGER_SIZE = 44;
785
958
  var MENU_MIN_WIDTH = 200;
@@ -792,8 +965,8 @@ var MENU_DIVIDER_HEIGHT = 9;
792
965
  var MENU_VERTICAL_PADDING = 12;
793
966
  function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accentColor, mutedColor, onAccentColor, dark, align: alignOverride, autoDismiss = true, labelFontFamily = MONO_FONT, allClearLabels = { all: "All", clear: "Clear" } }) {
794
967
  const menuBg = dark ? "#000000" : "#FFFFFF";
795
- const menuBorderColor = (0, import_auto_paper4.getBlendedColor)(mutedColor, menuBg, 0.5);
796
- const resolvedOnAccentColor = onAccentColor ?? (0, import_auto_paper4.getColorRoles)(accentColor, menuBg).onColor;
968
+ const menuBorderColor = (0, import_auto_paper6.getBlendedColor)(mutedColor, menuBg, 0.5);
969
+ const resolvedOnAccentColor = onAccentColor ?? (0, import_auto_paper6.getColorRoles)(accentColor, menuBg).onColor;
797
970
  const fg = dark ? "#FFFFFF" : "#000000";
798
971
  const open = host.openId === id;
799
972
  const estimatedHeight = MENU_VERTICAL_PADDING + sections.reduce((sum, section, sectionIndex) => {
@@ -825,17 +998,17 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
825
998
  }
826
999
  gaugeOffset += section.options.length;
827
1000
  }
828
- const anchorStyle = [styles8.anchor, open && styles8.anchorOpen];
1001
+ const anchorStyle = [styles10.anchor, open && styles10.anchorOpen];
829
1002
  const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
830
1003
  const dividerStyle = { backgroundColor: menuBorderColor };
831
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: anchorStyle, children: [
832
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { ref: triggerRef, collapsable: false, style: styles8.triggerBox, children: [
833
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
834
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press6.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
1004
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native13.View, { style: anchorStyle, children: [
1005
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native13.View, { ref: triggerRef, collapsable: false, style: styles10.triggerBox, children: [
1006
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
1007
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_feedback_press7.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
835
1008
  ] }),
836
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native11.ScrollView, { style: [styles8.menu, menuStyle], contentContainerStyle: styles8.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { children: [
837
- sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native11.View, { style: [styles8.divider, dividerStyle] }),
838
- section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1009
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.ScrollView, { style: [styles10.menu, menuStyle], contentContainerStyle: styles10.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native13.View, { children: [
1010
+ sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { style: [styles10.divider, dividerStyle] }),
1011
+ section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
839
1012
  SingleSection,
840
1013
  {
841
1014
  section,
@@ -848,7 +1021,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
848
1021
  if (autoDismiss) host.close();
849
1022
  }
850
1023
  }
851
- ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
1024
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
852
1025
  ] }, section.id)) }) })
853
1026
  ] });
854
1027
  }
@@ -860,17 +1033,17 @@ function SingleSection({
860
1033
  labelFontFamily,
861
1034
  onSelect
862
1035
  }) {
863
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: section.options.map((option) => {
1036
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, { children: section.options.map((option) => {
864
1037
  const selected = option.value === section.value;
865
1038
  const rowColor = selected ? onAccentColor : mutedColor;
866
- const itemStyle = [styles8.item, selected && { backgroundColor: accentColor }];
1039
+ const itemStyle = [styles10.item, selected && { backgroundColor: accentColor }];
867
1040
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
868
1041
  const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
869
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press6.TouchableRipple, { onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: styles8.itemRow, children: [
870
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
871
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: styles8.itemLabelColumn, children: [
872
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
873
- option.description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
1042
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native13.View, { style: styles10.itemRow, children: [
1043
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper7.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1044
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native13.View, { style: styles10.itemLabelColumn, children: [
1045
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper7.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
1046
+ option.description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper7.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
874
1047
  ] })
875
1048
  ] }) }, option.value);
876
1049
  }) });
@@ -884,23 +1057,23 @@ function MultiSection({
884
1057
  allClearLabels
885
1058
  }) {
886
1059
  const allSelected = section.value.length === section.options.length;
887
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1060
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
888
1061
  section.options.map((option, i) => {
889
1062
  const selected = section.value.includes(option.value);
890
1063
  const rowColor = selected ? onAccentColor : mutedColor;
891
1064
  const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
892
1065
  const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
893
- const itemStyle = [styles8.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
1066
+ const itemStyle = [styles10.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
894
1067
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
895
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press6.TouchableRipple, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: styles8.itemRow, children: [
896
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
897
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Text, { variant: "labelLarge", style: [styles8.itemLabelFlex, labelStyle], children: option.label })
1068
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native13.View, { style: styles10.itemRow, children: [
1069
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper7.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1070
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper7.Text, { variant: "labelLarge", style: [styles10.itemLabelFlex, labelStyle], children: option.label })
898
1071
  ] }) }, option.value);
899
1072
  }),
900
- section.allClear && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press6.TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles8.item, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Text, { variant: "labelLarge", style: [styles8.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
1073
+ section.allClear && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles10.item, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper7.Text, { variant: "labelLarge", style: [styles10.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
901
1074
  ] });
902
1075
  }
903
- var styles8 = import_react_native11.StyleSheet.create({
1076
+ var styles10 = import_react_native13.StyleSheet.create({
904
1077
  allClearLabel: {
905
1078
  fontWeight: "bold",
906
1079
  textAlign: "center"
@@ -917,7 +1090,7 @@ var styles8 = import_react_native11.StyleSheet.create({
917
1090
  zIndex: 100
918
1091
  },
919
1092
  divider: {
920
- height: import_react_native11.StyleSheet.hairlineWidth,
1093
+ height: import_react_native13.StyleSheet.hairlineWidth,
921
1094
  marginVertical: 4
922
1095
  },
923
1096
  item: {
@@ -965,21 +1138,21 @@ var styles8 = import_react_native11.StyleSheet.create({
965
1138
  });
966
1139
 
967
1140
  // src/SharedActionBand.tsx
968
- var import_feedback_press7 = require("@rific/feedback-press");
969
- var import_react_native12 = require("react-native");
970
- var import_jsx_runtime11 = require("react/jsx-runtime");
1141
+ var import_feedback_press8 = require("@rific/feedback-press");
1142
+ var import_react_native14 = require("react-native");
1143
+ var import_jsx_runtime13 = require("react/jsx-runtime");
971
1144
  function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
972
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native12.View, { style: [styles9.column, popoverOpen && styles9.columnOpen], children: [
973
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native12.View, { style: styles9.actionsRow, children: [
974
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_feedback_press7.IconButton, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
975
- onRandomize && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_feedback_press7.IconButton, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
976
- onReset && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_feedback_press7.IconButton, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
977
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_feedback_press7.IconButton, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
1145
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native14.View, { style: [styles11.column, popoverOpen && styles11.columnOpen], children: [
1146
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native14.View, { style: styles11.actionsRow, children: [
1147
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press8.IconButton, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1148
+ onRandomize && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press8.IconButton, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1149
+ onReset && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press8.IconButton, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
1150
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press8.IconButton, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
978
1151
  ] }),
979
1152
  children
980
1153
  ] });
981
1154
  }
982
- var styles9 = import_react_native12.StyleSheet.create({
1155
+ var styles11 = import_react_native14.StyleSheet.create({
983
1156
  actionsRow: {
984
1157
  alignItems: "center",
985
1158
  flexDirection: "row",
@@ -994,22 +1167,75 @@ var styles9 = import_react_native12.StyleSheet.create({
994
1167
  }
995
1168
  });
996
1169
 
1170
+ // src/StatRow.tsx
1171
+ var import_auto_paper7 = require("@rific/auto-paper");
1172
+ var import_react_native15 = require("react-native");
1173
+ var import_react_native_paper8 = require("react-native-paper");
1174
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1175
+ function StatRow({ label, value }) {
1176
+ const { dark } = (0, import_auto_paper7.useAutoPaperTheme)();
1177
+ const fg = dark ? "#FFFFFF" : "#000000";
1178
+ const fgMuted = dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)";
1179
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native15.View, { style: styles12.row, children: [
1180
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Text, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
1181
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Text, { variant: "bodyMedium", style: [styles12.value, { color: fg }], children: value })
1182
+ ] });
1183
+ }
1184
+ var styles12 = import_react_native15.StyleSheet.create({
1185
+ row: {
1186
+ alignItems: "center",
1187
+ flexDirection: "row",
1188
+ justifyContent: "space-between"
1189
+ },
1190
+ value: {
1191
+ fontWeight: "bold"
1192
+ }
1193
+ });
1194
+
1195
+ // src/StatSection.tsx
1196
+ var import_auto_paper8 = require("@rific/auto-paper");
1197
+ var import_react_native16 = require("react-native");
1198
+ var import_react_native_paper9 = require("react-native-paper");
1199
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1200
+ function StatSection({ label, children }) {
1201
+ const { dark } = (0, import_auto_paper8.useAutoPaperTheme)();
1202
+ const fgMuted = dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)";
1203
+ const sectionBg = dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)";
1204
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { style: [styles13.section, { backgroundColor: sectionBg }], children: [
1205
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper9.Text, { variant: "labelMedium", style: [styles13.label, { color: fgMuted }], children: label }),
1206
+ children
1207
+ ] });
1208
+ }
1209
+ var styles13 = import_react_native16.StyleSheet.create({
1210
+ label: {
1211
+ letterSpacing: 2
1212
+ },
1213
+ section: {
1214
+ borderRadius: 12,
1215
+ gap: 8,
1216
+ padding: 16
1217
+ }
1218
+ });
1219
+
997
1220
  // src/usePopoverHost.ts
998
- var import_react5 = require("react");
1221
+ var import_react6 = require("react");
999
1222
  function usePopoverHost() {
1000
- const [openId, setOpenId] = (0, import_react5.useState)(null);
1001
- const toggle = (0, import_react5.useCallback)((id) => {
1223
+ const [openId, setOpenId] = (0, import_react6.useState)(null);
1224
+ const toggle = (0, import_react6.useCallback)((id) => {
1002
1225
  setOpenId((prev) => prev === id ? null : id);
1003
1226
  }, []);
1004
- const close = (0, import_react5.useCallback)(() => setOpenId(null), []);
1227
+ const close = (0, import_react6.useCallback)(() => setOpenId(null), []);
1005
1228
  return { openId, toggle, close };
1006
1229
  }
1007
1230
  // Annotate the CommonJS export names for ESM import in node:
1008
1231
  0 && (module.exports = {
1232
+ AchievementRow,
1009
1233
  BaseSettingsDialog,
1234
+ BaseStatsScreen,
1010
1235
  CornerActionButtons,
1011
1236
  InlineColorPicker,
1012
1237
  LABELED_DROPDOWN_POPOVER_WIDTH,
1238
+ LOCKED_BADGE_COLOR,
1013
1239
  LabeledDropdown,
1014
1240
  MONO_FONT,
1015
1241
  PopoverBody,
@@ -1017,6 +1243,8 @@ function usePopoverHost() {
1017
1243
  ReadyButton,
1018
1244
  SectionedDropdown,
1019
1245
  SharedActionBand,
1246
+ StatRow,
1247
+ StatSection,
1020
1248
  TriggerGaugeHost,
1021
1249
  getLabeledDropdownContentHeight,
1022
1250
  loadSkiaWeb,