@tastic/hud 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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, fg: fgOverride, fgMuted: fgMutedOverride, sectionBg: sectionBgOverride }) {
191
+ const { dark } = (0, import_auto_paper.useAutoPaperTheme)();
192
+ const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
193
+ const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
194
+ const sectionBg = sectionBgOverride ?? (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,118 @@ 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", titleVariant = "headlineSmall", onBack, insets, onReset, resetLabel = "Reset All Stats", resetConfirmTitle = "Reset Everything?", resetConfirmBody = "This permanently erases all stats and achievements. This cannot be undone.", fg: fgOverride, bg: bgOverride, cardBg: cardBgOverride, accentColor: accentColorOverride, children }) {
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 = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
466
+ const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
467
+ const cardBg = cardBgOverride ?? (dark ? "#111111" : "#F2F2F2");
468
+ const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
469
+ const accentColor = accentColorOverride ?? colors.secondary;
470
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [styles3.container, { backgroundColor: bg }], children: [
471
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [styles3.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
472
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
473
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: titleVariant, style: [styles3.title, { color: fg }], children: title })
474
+ ] }),
475
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.ScrollView, { style: styles3.scrollView, contentContainerStyle: [styles3.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
476
+ children,
477
+ showReset && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.Button, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.secondary, textColor: colors.onSecondary, style: styles3.resetButton, children: resetLabel })
478
+ ] }),
479
+ 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: [
480
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Icon, { source: "alert-outline", size: 64, color: accentColor }),
481
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "headlineLarge", style: [styles3.overlayTitle, { color: accentColor }], children: resetConfirmTitle }),
482
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "bodyLarge", style: [styles3.overlayBody, { color: fg }], children: resetConfirmBody }),
483
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.Button, { mode: "contained", onPress: () => setConfirmResetVisible(false), style: styles3.overlayButton, children: "Cancel" }),
484
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
485
+ import_feedback_press2.Button,
486
+ {
487
+ mode: "contained",
488
+ onPress: () => {
489
+ onReset?.();
490
+ setConfirmResetVisible(false);
491
+ },
492
+ style: styles3.overlayButton,
493
+ buttonColor: colors.secondary,
494
+ textColor: colors.onSecondary,
495
+ children: "Reset"
496
+ }
497
+ )
498
+ ] }) }) })
499
+ ] });
500
+ }
501
+ var styles3 = import_react_native3.StyleSheet.create({
502
+ container: {
503
+ flex: 1
504
+ },
505
+ content: {
506
+ gap: 12,
507
+ paddingHorizontal: 20
508
+ },
509
+ header: {
510
+ alignItems: "center",
511
+ flexDirection: "row",
512
+ gap: 4,
513
+ paddingBottom: 8
514
+ },
515
+ // Same overlay shape BaseSettingsDialog's own info overlay uses (backdrop + centered card),
516
+ // rotated in place the same way rather than via Portal-root transform for the same reason — see
517
+ // that component's own identical styles for the full rationale.
518
+ overlay: {
519
+ alignItems: "center",
520
+ backgroundColor: "rgba(0,0,0,0.72)",
521
+ bottom: 0,
522
+ justifyContent: "center",
523
+ left: 0,
524
+ position: "absolute",
525
+ right: 0,
526
+ top: 0
527
+ },
528
+ overlayBody: { textAlign: "center" },
529
+ overlayButton: { width: 160 },
530
+ overlayCard: {
531
+ alignItems: "center",
532
+ borderRadius: 20,
533
+ borderWidth: 1,
534
+ gap: 16,
535
+ maxWidth: 360,
536
+ padding: 32
537
+ },
538
+ overlayTitle: { fontWeight: "bold", marginBottom: -8 },
539
+ resetButton: {
540
+ marginTop: 16
541
+ },
542
+ // Bounds the ScrollView to the space `container`'s flex:1 actually gives it — without this, a
543
+ // ScrollView with only a contentContainerStyle isn't reliably height-constrained on native (it
544
+ // can render at its full unclipped content height instead of the screen's), which leaves the
545
+ // header's back button touch target unreliable underneath it.
546
+ scrollView: {
547
+ flex: 1
548
+ },
549
+ title: {
550
+ flexShrink: 1,
551
+ fontWeight: "bold"
552
+ }
553
+ });
554
+
555
+ // src/CornerActionButtons.tsx
556
+ var import_feedback_press3 = require("@rific/feedback-press");
557
+ var import_react_native4 = require("react-native");
558
+ var import_jsx_runtime4 = require("react/jsx-runtime");
385
559
  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" })
560
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
561
+ /* @__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" }),
562
+ /* @__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
563
  ] });
390
564
  }
391
- var styles2 = import_react_native2.StyleSheet.create({
565
+ var styles4 = import_react_native4.StyleSheet.create({
392
566
  back: {
393
567
  position: "absolute"
394
568
  },
@@ -398,41 +572,41 @@ var styles2 = import_react_native2.StyleSheet.create({
398
572
  });
399
573
 
400
574
  // 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" });
575
+ var import_react_native5 = require("react-native");
576
+ var MONO_FONT = import_react_native5.Platform.select({ ios: "Menlo", android: "monospace", default: "monospace" });
403
577
 
404
578
  // src/InlineColorPicker.tsx
405
- var import_auto_paper2 = require("@rific/auto-paper");
406
- var import_feedback_press3 = require("@rific/feedback-press");
579
+ var import_auto_paper4 = require("@rific/auto-paper");
580
+ var import_feedback_press4 = require("@rific/feedback-press");
407
581
  var import_core2 = require("@tastic/core");
408
- var import_react_native6 = require("react-native");
409
- var import_react_native_paper2 = require("react-native-paper");
582
+ var import_react_native8 = require("react-native");
583
+ var import_react_native_paper4 = require("react-native-paper");
410
584
 
411
585
  // src/PopoverBody.tsx
412
- var import_react_native4 = require("react-native");
413
- var import_jsx_runtime3 = require("react/jsx-runtime");
586
+ var import_react_native6 = require("react-native");
587
+ var import_jsx_runtime5 = require("react/jsx-runtime");
414
588
  var CARET_SIZE = 7;
415
589
  var CARET_RING_OFFSET = 2;
416
590
  var CARET_DIP = 1;
417
591
  function PopoverBody({ visible, children, align = "center", verticalAlign = "below", caretColor, caretBorderColor, caretBorderWidth = 1, triggerSize = 0 }) {
418
592
  if (!visible) return null;
419
593
  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;
594
+ const alignStyle = align === "left" ? styles5.contentLeft : align === "right" ? styles5.contentRight : styles5.contentCenter;
595
+ const verticalStyle = above ? styles5.contentAbove : styles5.contentBelow;
422
596
  const ringSize = CARET_SIZE + caretBorderWidth;
423
597
  const caretOffset = (halfFootprint) => align === "right" ? { right: triggerSize / 2 - halfFootprint } : { left: triggerSize / 2 - halfFootprint };
424
598
  const caretFill = above ? "borderTopColor" : "borderBottomColor";
425
599
  const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
426
600
  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: [
601
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native6.View, { style: [styles5.content, alignStyle, verticalStyle], children: [
428
602
  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 }] })
603
+ caretColor && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
604
+ /* @__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 }] }),
605
+ /* @__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
606
  ] })
433
607
  ] });
434
608
  }
435
- var styles3 = import_react_native4.StyleSheet.create({
609
+ var styles5 = import_react_native6.StyleSheet.create({
436
610
  caret: {
437
611
  borderLeftColor: "transparent",
438
612
  borderRightColor: "transparent",
@@ -475,22 +649,22 @@ var styles3 = import_react_native4.StyleSheet.create({
475
649
  });
476
650
 
477
651
  // src/useAutoAlign.ts
478
- var import_react2 = require("react");
479
- var import_react_native5 = require("react-native");
652
+ var import_react3 = require("react");
653
+ var import_react_native7 = require("react-native");
480
654
  var EDGE_MARGIN = 12;
481
655
  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);
656
+ const triggerRef = (0, import_react3.useRef)(null);
657
+ const { width: windowWidth, height: windowHeight } = (0, import_react_native7.useWindowDimensions)();
658
+ const [align, setAlign] = (0, import_react3.useState)("center");
659
+ const [verticalAlign, setVerticalAlign] = (0, import_react3.useState)("below");
660
+ const [maxHeight, setMaxHeight] = (0, import_react3.useState)(contentHeight);
661
+ const [measured, setMeasured] = (0, import_react3.useState)(false);
662
+ const [prevOpen, setPrevOpen] = (0, import_react3.useState)(open);
489
663
  if (open !== prevOpen) {
490
664
  setPrevOpen(open);
491
665
  if (open) setMeasured(false);
492
666
  }
493
- const measure = (0, import_react2.useCallback)(() => {
667
+ const measure = (0, import_react3.useCallback)(() => {
494
668
  triggerRef.current?.measureInWindow((x, y, triggerWidth, triggerHeight) => {
495
669
  const centerX = x + triggerWidth / 2;
496
670
  const overflowsRight = centerX + contentWidth / 2 > windowWidth - EDGE_MARGIN;
@@ -506,14 +680,14 @@ function useAutoAlign(open, contentWidth, contentHeight) {
506
680
  setMeasured(true);
507
681
  });
508
682
  }, [contentWidth, contentHeight, windowWidth, windowHeight]);
509
- (0, import_react2.useEffect)(() => {
683
+ (0, import_react3.useEffect)(() => {
510
684
  if (open) measure();
511
685
  }, [open, measure]);
512
686
  return { align, maxHeight, measured, triggerRef, verticalAlign };
513
687
  }
514
688
 
515
689
  // src/InlineColorPicker.tsx
516
- var import_jsx_runtime4 = require("react/jsx-runtime");
690
+ var import_jsx_runtime6 = require("react/jsx-runtime");
517
691
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
518
692
  var DEFAULT_SIZE = 48;
519
693
  var SWATCH_SIZE = 28;
@@ -526,9 +700,9 @@ var SCREEN_MARGIN = 40;
526
700
  function widthForColumns(columns) {
527
701
  return SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * columns + SWATCHES_GAP * (columns - 1);
528
702
  }
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 }) {
703
+ 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
704
  const menuBg = dark ? "#000000" : "#FFFFFF";
531
- const { width: windowWidth } = (0, import_react_native6.useWindowDimensions)();
705
+ const { width: windowWidth } = (0, import_react_native8.useWindowDimensions)();
532
706
  const autoColumns = (0, import_core2.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
533
707
  const resolvedColumns = columns ?? autoColumns;
534
708
  const swatchesWidth = widthForColumns(resolvedColumns);
@@ -537,16 +711,16 @@ function InlineColorPicker({ id, host, value, onChange, swatches = import_auto_p
537
711
  const open = host.openId === id;
538
712
  const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, swatchesWidth, swatchesHeight);
539
713
  const align = alignOverride ?? autoAlign;
540
- const contrastColor = (0, import_auto_paper2.getContrastColor)(value);
714
+ const contrastColor = (0, import_auto_paper4.getContrastColor)(value);
541
715
  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) => {
716
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native8.View, { style: [styles6.anchor, open && styles6.anchorOpen], children: [
717
+ /* @__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 }) }) }),
718
+ /* @__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
719
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
546
720
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
547
721
  const swappable = taken && allowSwapTaken;
548
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
549
- import_feedback_press3.TouchableRipple,
722
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
723
+ import_feedback_press4.TouchableRipple,
550
724
  {
551
725
  disabled: taken && !swappable,
552
726
  onPress: () => {
@@ -554,15 +728,15 @@ function InlineColorPicker({ id, host, value, onChange, swatches = import_auto_p
554
728
  if (autoDismiss) host.close();
555
729
  },
556
730
  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, {})
731
+ style: [styles6.swatch, { backgroundColor: swatch.value }, selected && styles6.swatchSelected, taken && !swappable && styles6.swatchTaken],
732
+ 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
733
  },
560
734
  swatch.value
561
735
  );
562
736
  }) }) })
563
737
  ] });
564
738
  }
565
- var styles4 = import_react_native6.StyleSheet.create({
739
+ var styles6 = import_react_native8.StyleSheet.create({
566
740
  anchor: {
567
741
  position: "relative"
568
742
  },
@@ -634,11 +808,11 @@ var styles4 = import_react_native6.StyleSheet.create({
634
808
  });
635
809
 
636
810
  // 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");
811
+ var import_auto_paper5 = require("@rific/auto-paper");
812
+ var import_feedback_press5 = require("@rific/feedback-press");
813
+ var import_react_native9 = require("react-native");
814
+ var import_react_native_paper5 = require("react-native-paper");
815
+ var import_jsx_runtime7 = require("react/jsx-runtime");
642
816
  var TRIGGER_HEIGHT = 28;
643
817
  var POPOVER_WIDTH = 180;
644
818
  var ROW_HEIGHT = 36;
@@ -661,22 +835,22 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
661
835
  onChange(option.value);
662
836
  host.close();
663
837
  };
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 })
838
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native9.View, { style: [styles7.anchor, open && styles7.anchorOpen], children: [
839
+ /* @__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: [
840
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper5.Text, { style: [styles7.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
841
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper5.Icon, { source: "menu-down", size: 16, color })
668
842
  ] }) }),
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) => {
843
+ /* @__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
844
  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 })
845
+ const onColor = (0, import_auto_paper5.getContrastColor)(color);
846
+ 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: [
847
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper5.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
848
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper5.Text, { style: [styles7.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
675
849
  ] }) }, option.value);
676
850
  }) }) })
677
851
  ] });
678
852
  }
679
- var styles5 = import_react_native7.StyleSheet.create({
853
+ var styles7 = import_react_native9.StyleSheet.create({
680
854
  anchor: {
681
855
  position: "relative"
682
856
  },
@@ -734,25 +908,25 @@ function loadSkiaWeb() {
734
908
  }
735
909
 
736
910
  // src/PressAwayOverlay.tsx
737
- var import_react_native8 = require("react-native");
738
- var import_jsx_runtime6 = require("react/jsx-runtime");
911
+ var import_react_native10 = require("react-native");
912
+ var import_jsx_runtime8 = require("react/jsx-runtime");
739
913
  function PressAwayOverlay({ active, onPress, style }) {
740
914
  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] });
915
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native10.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native10.StyleSheet.absoluteFill, style] });
742
916
  }
743
917
 
744
918
  // 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");
919
+ var import_feedback_press6 = require("@rific/feedback-press");
920
+ var import_react_native11 = require("react-native");
921
+ var import_react_native_paper6 = require("react-native-paper");
922
+ var import_jsx_runtime9 = require("react/jsx-runtime");
749
923
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
750
- const readyButtonStyle = [styles6.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
924
+ const readyButtonStyle = [styles8.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
751
925
  const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
752
926
  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?" }) });
927
+ 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
928
  }
755
- var styles6 = import_react_native9.StyleSheet.create({
929
+ var styles8 = import_react_native11.StyleSheet.create({
756
930
  readyButton: {
757
931
  alignItems: "center",
758
932
  borderRadius: 12,
@@ -765,21 +939,21 @@ var styles6 = import_react_native9.StyleSheet.create({
765
939
  });
766
940
 
767
941
  // 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");
942
+ var import_auto_paper6 = require("@rific/auto-paper");
943
+ var import_feedback_press7 = require("@rific/feedback-press");
944
+ var import_react_native13 = require("react-native");
945
+ var import_react_native_paper7 = require("react-native-paper");
772
946
 
773
947
  // 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 }))));
948
+ var import_react5 = require("react");
949
+ var import_jsx_runtime11 = require("react/jsx-runtime");
950
+ var LazyTriggerGauge = (0, import_react5.lazy)(() => loadSkiaWeb().then(() => Promise.resolve().then(() => (init_TriggerGauge(), TriggerGauge_exports)).then((m) => ({ default: m.TriggerGauge }))));
777
951
  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 }) });
952
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react5.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(LazyTriggerGauge, { ...props }) });
779
953
  }
780
954
 
781
955
  // src/SectionedDropdown.tsx
782
- var import_jsx_runtime10 = require("react/jsx-runtime");
956
+ var import_jsx_runtime12 = require("react/jsx-runtime");
783
957
  var MENU_BORDER_WIDTH = 1;
784
958
  var TRIGGER_SIZE = 44;
785
959
  var MENU_MIN_WIDTH = 200;
@@ -792,8 +966,8 @@ var MENU_DIVIDER_HEIGHT = 9;
792
966
  var MENU_VERTICAL_PADDING = 12;
793
967
  function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accentColor, mutedColor, onAccentColor, dark, align: alignOverride, autoDismiss = true, labelFontFamily = MONO_FONT, allClearLabels = { all: "All", clear: "Clear" } }) {
794
968
  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;
969
+ const menuBorderColor = (0, import_auto_paper6.getBlendedColor)(mutedColor, menuBg, 0.5);
970
+ const resolvedOnAccentColor = onAccentColor ?? (0, import_auto_paper6.getColorRoles)(accentColor, menuBg).onColor;
797
971
  const fg = dark ? "#FFFFFF" : "#000000";
798
972
  const open = host.openId === id;
799
973
  const estimatedHeight = MENU_VERTICAL_PADDING + sections.reduce((sum, section, sectionIndex) => {
@@ -825,17 +999,17 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
825
999
  }
826
1000
  gaugeOffset += section.options.length;
827
1001
  }
828
- const anchorStyle = [styles8.anchor, open && styles8.anchorOpen];
1002
+ const anchorStyle = [styles10.anchor, open && styles10.anchorOpen];
829
1003
  const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
830
1004
  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) })
1005
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native13.View, { style: anchorStyle, children: [
1006
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native13.View, { ref: triggerRef, collapsable: false, style: styles10.triggerBox, children: [
1007
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
1008
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_feedback_press7.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
835
1009
  ] }),
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)(
1010
+ /* @__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: [
1011
+ sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { style: [styles10.divider, dividerStyle] }),
1012
+ section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
839
1013
  SingleSection,
840
1014
  {
841
1015
  section,
@@ -848,7 +1022,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
848
1022
  if (autoDismiss) host.close();
849
1023
  }
850
1024
  }
851
- ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
1025
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
852
1026
  ] }, section.id)) }) })
853
1027
  ] });
854
1028
  }
@@ -860,17 +1034,17 @@ function SingleSection({
860
1034
  labelFontFamily,
861
1035
  onSelect
862
1036
  }) {
863
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: section.options.map((option) => {
1037
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, { children: section.options.map((option) => {
864
1038
  const selected = option.value === section.value;
865
1039
  const rowColor = selected ? onAccentColor : mutedColor;
866
- const itemStyle = [styles8.item, selected && { backgroundColor: accentColor }];
1040
+ const itemStyle = [styles10.item, selected && { backgroundColor: accentColor }];
867
1041
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
868
1042
  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 })
1043
+ 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: [
1044
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper7.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1045
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native13.View, { style: styles10.itemLabelColumn, children: [
1046
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper7.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
1047
+ option.description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper7.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
874
1048
  ] })
875
1049
  ] }) }, option.value);
876
1050
  }) });
@@ -884,23 +1058,23 @@ function MultiSection({
884
1058
  allClearLabels
885
1059
  }) {
886
1060
  const allSelected = section.value.length === section.options.length;
887
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1061
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
888
1062
  section.options.map((option, i) => {
889
1063
  const selected = section.value.includes(option.value);
890
1064
  const rowColor = selected ? onAccentColor : mutedColor;
891
1065
  const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
892
1066
  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 }];
1067
+ const itemStyle = [styles10.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
894
1068
  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 })
1069
+ 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: [
1070
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper7.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1071
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper7.Text, { variant: "labelLarge", style: [styles10.itemLabelFlex, labelStyle], children: option.label })
898
1072
  ] }) }, option.value);
899
1073
  }),
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 }) })
1074
+ 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
1075
  ] });
902
1076
  }
903
- var styles8 = import_react_native11.StyleSheet.create({
1077
+ var styles10 = import_react_native13.StyleSheet.create({
904
1078
  allClearLabel: {
905
1079
  fontWeight: "bold",
906
1080
  textAlign: "center"
@@ -917,7 +1091,7 @@ var styles8 = import_react_native11.StyleSheet.create({
917
1091
  zIndex: 100
918
1092
  },
919
1093
  divider: {
920
- height: import_react_native11.StyleSheet.hairlineWidth,
1094
+ height: import_react_native13.StyleSheet.hairlineWidth,
921
1095
  marginVertical: 4
922
1096
  },
923
1097
  item: {
@@ -965,21 +1139,21 @@ var styles8 = import_react_native11.StyleSheet.create({
965
1139
  });
966
1140
 
967
1141
  // 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");
1142
+ var import_feedback_press8 = require("@rific/feedback-press");
1143
+ var import_react_native14 = require("react-native");
1144
+ var import_jsx_runtime13 = require("react/jsx-runtime");
971
1145
  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" })
1146
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native14.View, { style: [styles11.column, popoverOpen && styles11.columnOpen], children: [
1147
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native14.View, { style: styles11.actionsRow, children: [
1148
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press8.IconButton, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1149
+ onRandomize && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press8.IconButton, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1150
+ 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" }),
1151
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press8.IconButton, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
978
1152
  ] }),
979
1153
  children
980
1154
  ] });
981
1155
  }
982
- var styles9 = import_react_native12.StyleSheet.create({
1156
+ var styles11 = import_react_native14.StyleSheet.create({
983
1157
  actionsRow: {
984
1158
  alignItems: "center",
985
1159
  flexDirection: "row",
@@ -994,22 +1168,80 @@ var styles9 = import_react_native12.StyleSheet.create({
994
1168
  }
995
1169
  });
996
1170
 
1171
+ // src/StatRow.tsx
1172
+ var import_auto_paper7 = require("@rific/auto-paper");
1173
+ var import_react_native15 = require("react-native");
1174
+ var import_react_native_paper8 = require("react-native-paper");
1175
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1176
+ function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }) {
1177
+ const { dark } = (0, import_auto_paper7.useAutoPaperTheme)();
1178
+ const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
1179
+ const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
1180
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native15.View, { style: styles12.row, children: [
1181
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Text, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
1182
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Text, { variant: "bodyMedium", style: [styles12.value, { color: fg }], children: value })
1183
+ ] });
1184
+ }
1185
+ var styles12 = import_react_native15.StyleSheet.create({
1186
+ row: {
1187
+ alignItems: "center",
1188
+ flexDirection: "row",
1189
+ justifyContent: "space-between"
1190
+ },
1191
+ value: {
1192
+ fontWeight: "bold"
1193
+ }
1194
+ });
1195
+
1196
+ // src/StatSection.tsx
1197
+ var import_auto_paper8 = require("@rific/auto-paper");
1198
+ var import_react_native16 = require("react-native");
1199
+ var import_react_native_paper9 = require("react-native-paper");
1200
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1201
+ function StatSection({ label, fg: fgOverride, sectionBg: sectionBgOverride, children }) {
1202
+ const { dark } = (0, import_auto_paper8.useAutoPaperTheme)();
1203
+ const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
1204
+ const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
1205
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { style: [styles13.section, { backgroundColor: sectionBg }], children: [
1206
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper9.Text, { variant: "labelMedium", style: [styles13.label, { color: fg }], children: label }),
1207
+ children
1208
+ ] });
1209
+ }
1210
+ var styles13 = import_react_native16.StyleSheet.create({
1211
+ // Bold and full-contrast (fg), not the original fontless/fgMuted treatment — that read at the
1212
+ // same visual weight as StatRow's own row labels below it (both used the identical muted color;
1213
+ // letterSpacing/variant alone didn't separate them enough to read as "this is the group header"
1214
+ // at a glance). Matches AchievementRow's own title styling (bold + fg) below it in the same list.
1215
+ label: {
1216
+ fontWeight: "bold",
1217
+ letterSpacing: 2
1218
+ },
1219
+ section: {
1220
+ borderRadius: 12,
1221
+ gap: 8,
1222
+ padding: 16
1223
+ }
1224
+ });
1225
+
997
1226
  // src/usePopoverHost.ts
998
- var import_react5 = require("react");
1227
+ var import_react6 = require("react");
999
1228
  function usePopoverHost() {
1000
- const [openId, setOpenId] = (0, import_react5.useState)(null);
1001
- const toggle = (0, import_react5.useCallback)((id) => {
1229
+ const [openId, setOpenId] = (0, import_react6.useState)(null);
1230
+ const toggle = (0, import_react6.useCallback)((id) => {
1002
1231
  setOpenId((prev) => prev === id ? null : id);
1003
1232
  }, []);
1004
- const close = (0, import_react5.useCallback)(() => setOpenId(null), []);
1233
+ const close = (0, import_react6.useCallback)(() => setOpenId(null), []);
1005
1234
  return { openId, toggle, close };
1006
1235
  }
1007
1236
  // Annotate the CommonJS export names for ESM import in node:
1008
1237
  0 && (module.exports = {
1238
+ AchievementRow,
1009
1239
  BaseSettingsDialog,
1240
+ BaseStatsScreen,
1010
1241
  CornerActionButtons,
1011
1242
  InlineColorPicker,
1012
1243
  LABELED_DROPDOWN_POPOVER_WIDTH,
1244
+ LOCKED_BADGE_COLOR,
1013
1245
  LabeledDropdown,
1014
1246
  MONO_FONT,
1015
1247
  PopoverBody,
@@ -1017,6 +1249,8 @@ function usePopoverHost() {
1017
1249
  ReadyButton,
1018
1250
  SectionedDropdown,
1019
1251
  SharedActionBand,
1252
+ StatRow,
1253
+ StatSection,
1020
1254
  TriggerGaugeHost,
1021
1255
  getLabeledDropdownContentHeight,
1022
1256
  loadSkiaWeb,