@tastic/hud 0.8.2 → 0.8.4

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_react4.useLayoutEffect)(() => {
31
+ (0, import_react5.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_react4.useCallback)((i) => startDeg + (i + 0.5) * step, [startDeg, step]);
37
+ const centerDeg = (0, import_react5.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_react4.useState)(litIndices);
41
+ const [transitionFrom, setTransitionFrom] = (0, import_react5.useState)(litIndices);
42
42
  const transitionFromKey = [...transitionFrom].sort((a, b) => a - b).join(",");
43
- const lastKeyRef = (0, import_react4.useRef)(litIndicesKey);
44
- const lastIndicesRef = (0, import_react4.useRef)(litIndices);
43
+ const lastKeyRef = (0, import_react5.useRef)(litIndicesKey);
44
+ const lastIndicesRef = (0, import_react5.useRef)(litIndices);
45
45
  const selectionProgress = (0, import_react_native_reanimated.useSharedValue)(1);
46
- (0, import_react4.useLayoutEffect)(() => {
46
+ (0, import_react5.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) });
@@ -72,7 +72,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
72
72
  if (mountProgress.value < 1) {
73
73
  const startFrac = i * MOUNT_STAGGER_MS / totalDurationMs;
74
74
  const endFrac = startFrac + MOUNT_DURATION_MS / totalDurationMs;
75
- local = (0, import_core4.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
75
+ local = (0, import_core5.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
76
76
  }
77
77
  const sweep = dashAngleDeg * local;
78
78
  if (sweep <= 0) continue;
@@ -96,7 +96,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
96
96
  if (!isLitNow) continue;
97
97
  const startFrac = i * MOUNT_STAGGER_MS / totalDurationMs;
98
98
  const endFrac = startFrac + MOUNT_DURATION_MS / totalDurationMs;
99
- local = (0, import_core4.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
99
+ local = (0, import_core5.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
100
100
  } else if (isLitNow === wasLit) {
101
101
  local = 1;
102
102
  } else if (isLitNow) {
@@ -120,19 +120,19 @@ 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_native8.View, { style: [styles7.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
123
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native9.View, { style: [styles7.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
124
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
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 })
126
126
  ] }) });
127
127
  }
128
- var import_react_native_skia, import_core4, import_react4, import_react_native8, 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_core5, import_react5, import_react_native9, 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;
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
- import_core4 = require("@tastic/core");
134
- import_react4 = require("react");
135
- import_react_native8 = require("react-native");
133
+ import_core5 = require("@tastic/core");
134
+ import_react5 = require("react");
135
+ import_react_native9 = require("react-native");
136
136
  import_react_native_reanimated = require("react-native-reanimated");
137
137
  import_jsx_runtime8 = require("react/jsx-runtime");
138
138
  UNLIT_THICKNESS = 3;
@@ -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_native8.StyleSheet.create({
147
+ styles7 = import_react_native9.StyleSheet.create({
148
148
  container: {
149
149
  alignItems: "center",
150
150
  justifyContent: "center",
@@ -179,8 +179,11 @@ __export(index_exports, {
179
179
  StatRow: () => StatRow,
180
180
  StatSection: () => StatSection,
181
181
  TriggerGaugeHost: () => TriggerGaugeHost,
182
+ UpdateDialog: () => UpdateDialog,
182
183
  getLabeledDropdownContentHeight: () => getLabeledDropdownContentHeight,
184
+ getOverlayCardColors: () => getOverlayCardColors,
183
185
  loadSkiaWeb: () => loadSkiaWeb,
186
+ overlayStyles: () => overlayStyles,
184
187
  useAutoAlign: () => useAutoAlign,
185
188
  usePopoverHost: () => usePopoverHost
186
189
  });
@@ -255,64 +258,191 @@ var styles = import_react_native.StyleSheet.create({
255
258
  });
256
259
 
257
260
  // src/BaseSettingsDialog.tsx
261
+ var import_auto_paper3 = require("@rific/auto-paper");
262
+ var import_feedback_press2 = require("@rific/feedback-press");
263
+ var import_updater = require("@rific/updater");
264
+ var import_core2 = require("@tastic/core");
265
+ var import_react2 = require("react");
266
+ var import_react_native4 = require("react-native");
267
+ var import_react_native_paper3 = require("react-native-paper");
268
+
269
+ // src/ConfirmDialog.tsx
258
270
  var import_auto_paper2 = require("@rific/auto-paper");
259
271
  var import_feedback_press = require("@rific/feedback-press");
260
- var import_updater = require("@rific/updater");
261
272
  var import_core = require("@tastic/core");
262
- var import_react = require("react");
263
- var import_react_native2 = require("react-native");
273
+ var import_react_native3 = require("react-native");
264
274
  var import_react_native_paper2 = require("react-native-paper");
275
+
276
+ // src/overlayCard.ts
277
+ var import_react_native2 = require("react-native");
278
+ function getOverlayCardColors(dark) {
279
+ return {
280
+ fg: dark ? "#FFFFFF" : "#000000",
281
+ fgMuted: dark ? "rgba(255,255,255,0.4)" : "rgba(0,0,0,0.4)",
282
+ cardBg: dark ? "#111111" : "#F2F2F2",
283
+ cardBorder: dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)"
284
+ };
285
+ }
286
+ var overlayStyles = import_react_native2.StyleSheet.create({
287
+ card: {
288
+ alignItems: "center",
289
+ borderRadius: 20,
290
+ borderWidth: 1,
291
+ gap: 16,
292
+ maxWidth: 360,
293
+ padding: 32
294
+ },
295
+ overlay: {
296
+ alignItems: "center",
297
+ backgroundColor: "rgba(0,0,0,0.72)",
298
+ bottom: 0,
299
+ justifyContent: "center",
300
+ left: 0,
301
+ position: "absolute",
302
+ right: 0,
303
+ top: 0
304
+ }
305
+ });
306
+
307
+ // src/ConfirmDialog.tsx
265
308
  var import_jsx_runtime2 = require("react/jsx-runtime");
309
+ function ConfirmDialog({ visible, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, rotation = 0, icon = "help-circle-outline", destructive = false }) {
310
+ const { dark, colors } = (0, import_auto_paper2.useAutoPaperTheme)();
311
+ if (!visible) return null;
312
+ const { fg, cardBg, cardBorder } = getOverlayCardColors(dark);
313
+ const accent = destructive ? colors.danger : colors.primary;
314
+ const onAccent = destructive ? colors.onDanger : colors.onPrimary;
315
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.View, { style: [overlayStyles.overlay, styles2.overlay], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: [overlayStyles.card, styles2.card, { backgroundColor: cardBg, borderColor: cardBorder }, (0, import_core.toRotationStyle)(rotation)], children: [
316
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Icon, { source: icon, size: 64, color: accent }),
317
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "headlineLarge", style: [styles2.title, { color: accent }], children: title }),
318
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodyLarge", style: [styles2.body, { color: fg }], children: message }),
319
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: styles2.actions, children: [
320
+ cancelLabel !== void 0 && onCancel !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_feedback_press.Button, { mode: "outlined", onPress: onCancel, style: styles2.actionButton, children: cancelLabel }),
321
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_feedback_press.Button, { mode: "contained", onPress: onConfirm, style: styles2.actionButton, buttonColor: accent, textColor: onAccent, children: confirmLabel })
322
+ ] })
323
+ ] }) }) });
324
+ }
325
+ var styles2 = import_react_native3.StyleSheet.create({
326
+ // flexBasis (not flex: 1's own 0%) is each button's real floor: whenever both fit side by side,
327
+ // flexGrow still splits any leftover width evenly beyond that floor, same visual result as the
328
+ // old flex: 1 on a roomy screen - but a button can never be squeezed narrower than 128, only
329
+ // wrap to its own row instead (see actions' flexWrap below). react-native-paper's Button
330
+ // hardcodes numberOfLines={1} on its label with no override, so a button squeezed under its
331
+ // label's natural width silently ellipsizes ("Cancel" -> "Can...") instead of wrapping - assumed
332
+ // fixable by just giving the row more width, until confirmed live that these two-word labels
333
+ // (Cancel/Reset/New Game) can still lose the truncation fight even with the extra room card's own
334
+ // width fix below provides, on a narrow enough screen. flexBasis is the real fix: past a certain
335
+ // width there's no side-by-side arrangement that fits both buttons at their natural label size,
336
+ // so the layout has to give (stack) rather than the label (truncate).
337
+ actionButton: {
338
+ flexBasis: 128,
339
+ flexGrow: 1
340
+ },
341
+ actions: {
342
+ flexDirection: "row",
343
+ flexWrap: "wrap",
344
+ gap: 12,
345
+ width: "100%"
346
+ },
347
+ body: {
348
+ textAlign: "center"
349
+ },
350
+ // Additive on top of overlayCard's own shared `card` shape (borderRadius/borderWidth/gap/maxWidth/
351
+ // padding) — claims its full allowance instead of just maxWidth alone, which only caps how wide the
352
+ // card is allowed to get - without an explicit width, a flex-centered child sizes to its own content
353
+ // instead of stretching, so the card (and therefore actions below) can end up narrower than the
354
+ // screen actually has room for, needlessly forcing the action row to wrap sooner than it has to.
355
+ card: {
356
+ width: "100%"
357
+ },
358
+ // Additive on top of overlayCard's own shared `overlay` backdrop — gives the card room to breathe
359
+ // from the screen edges now that it reliably stretches to fill its full width allowance (see
360
+ // card's own width comment) - without this, width: '100%' would run the card edge-to-edge on a
361
+ // screen narrower than maxWidth's own 360.
362
+ overlay: {
363
+ paddingHorizontal: 24
364
+ },
365
+ // textAlign is load-bearing, not decorative, once `title` wraps to more than one line - centered
366
+ // within card's own alignItems: 'center' only centers the Text's block as a whole; each wrapped
367
+ // line inside that block still defaults to left-aligned without this.
368
+ title: {
369
+ fontWeight: "bold",
370
+ textAlign: "center"
371
+ }
372
+ });
373
+
374
+ // src/useUpdateConfirmBridge.ts
375
+ var import_react = require("react");
376
+ function useUpdateConfirmBridge() {
377
+ const [manifest, setManifest] = (0, import_react.useState)(null);
378
+ const resolveRef = (0, import_react.useRef)(null);
379
+ const onConfirm = (0, import_react.useCallback)(
380
+ (next) => new Promise((resolve) => {
381
+ resolveRef.current = resolve;
382
+ setManifest(next);
383
+ }),
384
+ []
385
+ );
386
+ const respond = (0, import_react.useCallback)((confirmed) => {
387
+ setManifest(null);
388
+ resolveRef.current?.(confirmed);
389
+ resolveRef.current = null;
390
+ }, []);
391
+ return { manifest, onConfirm, respond };
392
+ }
393
+
394
+ // src/BaseSettingsDialog.tsx
395
+ var import_jsx_runtime3 = require("react/jsx-runtime");
266
396
  function SettingIcon({ source, color, containerColor }) {
267
- 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 }) });
397
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native4.View, { style: [styles3.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Icon, { source, size: 18, color }) });
268
398
  }
269
399
  function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, children }) {
270
- const { dark, colors } = (0, import_auto_paper2.useAutoPaperTheme)();
271
- const ambientRotation = (0, import_core.useRotation)();
400
+ const { colors } = (0, import_auto_paper3.useAutoPaperTheme)();
401
+ const ambientRotation = (0, import_core2.useRotation)();
272
402
  const rotation = rotationOverride ?? ambientRotation;
273
- const isTouchPrimary = (0, import_core.useIsTouchPrimaryDevice)();
274
- const { settings: hapticSettings, set: setHapticSettings } = (0, import_feedback_press.useHapticSettings)();
275
- const { settings: soundSettings, set: setSoundSettings } = (0, import_feedback_press.useSoundSettings)();
276
- const sound = (0, import_react.useContext)(import_feedback_press.SoundContext);
277
- const { forceShort: forceHapticFeedback } = (0, import_feedback_press.useVibration)();
278
- const [infoMessage, setInfoMessage] = (0, import_react.useState)(null);
403
+ const isTouchPrimary = (0, import_core2.useIsTouchPrimaryDevice)();
404
+ const { settings: hapticSettings, set: setHapticSettings } = (0, import_feedback_press2.useHapticSettings)();
405
+ const { settings: soundSettings, set: setSoundSettings } = (0, import_feedback_press2.useSoundSettings)();
406
+ const sound = (0, import_react2.useContext)(import_feedback_press2.SoundContext);
407
+ const { forceShort: forceHapticFeedback } = (0, import_feedback_press2.useVibration)();
408
+ const [infoMessage, setInfoMessage] = (0, import_react2.useState)(null);
409
+ const { manifest: updateManifest, onConfirm: onUpdateConfirm, respond: respondToUpdateConfirm } = useUpdateConfirmBridge();
279
410
  const { check, checking, updateReady } = (0, import_updater.useUpdater)({
280
411
  autoCheck: false,
281
412
  autoPrompt: false,
413
+ onConfirm: onUpdateConfirm,
282
414
  onError: onUpdateError,
283
415
  onInfo: (title, message) => setInfoMessage({ title, message })
284
416
  });
417
+ const updateManifestDate = updateManifest ? new Date(updateManifest.createdAt) : null;
285
418
  const showLockOrientation = isTouchPrimary && lockOrientation !== void 0 && onLockOrientationChange !== void 0;
286
- const showEdgeGuard = import_react_native2.Platform.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
287
- const showHaptics = import_react_native2.Platform.OS !== "web" && !hideHaptics;
419
+ const showEdgeGuard = import_react_native4.Platform.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
420
+ const showHaptics = import_react_native4.Platform.OS !== "web" && !hideHaptics;
288
421
  const showSound = !hideSound;
289
422
  const showAppearance = !hideAppearance;
290
- const showUpdateCheck = import_react_native2.Platform.OS !== "web" && !hideUpdateCheck;
291
- const fg = dark ? "#FFFFFF" : "#000000";
292
- const cardBg = dark ? "#111111" : "#F2F2F2";
293
- const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
294
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
295
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_auto_paper2.Dialog, { visible, onDismiss, style: [styles2.dialog, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
296
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_auto_paper2.Dialog.Title, { children: "Settings" }),
297
- /* @__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: [
298
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.toggleGroup, children: [
299
- 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: [
300
- /* @__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 }),
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: "Lock Orientation" }),
303
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
423
+ const showUpdateCheck = import_react_native4.Platform.OS !== "web" && !hideUpdateCheck;
424
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
425
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_auto_paper3.Dialog, { visible, onDismiss, style: [styles3.dialog, (0, import_core2.toRotationStyle)(rotation)], children: [
426
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_auto_paper3.Dialog.Title, { children: "Settings" }),
427
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_auto_paper3.Dialog.ScrollArea, { style: styles3.scrollArea, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.ScrollView, { contentContainerStyle: styles3.content, showsVerticalScrollIndicator: false, children: [
428
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.toggleGroup, children: [
429
+ showLockOrientation && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles3.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.toggleContent, children: [
430
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
431
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.flexShrink, children: [
432
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
433
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
304
434
  ] })
305
435
  ] }) }),
306
- 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: [
307
- /* @__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 }),
308
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.flexShrink, children: [
309
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
310
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
436
+ showEdgeGuard && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles3.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.toggleContent, children: [
437
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
438
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.flexShrink, children: [
439
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
440
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
311
441
  ] })
312
442
  ] }) }),
313
- (showSound || showHaptics) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.toggleRow, children: [
314
- showSound && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
315
- import_feedback_press.TouchableRipple,
443
+ (showSound || showHaptics) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.toggleRow, children: [
444
+ showSound && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
445
+ import_feedback_press2.TouchableRipple,
316
446
  {
317
447
  soundDisabled: true,
318
448
  onPress: () => {
@@ -320,16 +450,16 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
320
450
  setSoundSettings({ enabled });
321
451
  if (enabled) sound.selection?.();
322
452
  },
323
- style: styles2.toggleButtonInRow,
453
+ style: styles3.toggleButtonInRow,
324
454
  accessibilityLabel: `Sound ${soundSettings.enabled ? "on" : "off"}`,
325
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.toggleContent, children: [
326
- /* @__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 }),
327
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
455
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.toggleContent, children: [
456
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
457
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
328
458
  ] })
329
459
  }
330
460
  ),
331
- showHaptics && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
332
- import_feedback_press.TouchableRipple,
461
+ showHaptics && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
462
+ import_feedback_press2.TouchableRipple,
333
463
  {
334
464
  hapticDisabled: true,
335
465
  onPress: () => {
@@ -337,40 +467,36 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
337
467
  setHapticSettings({ vibrate });
338
468
  if (vibrate) forceHapticFeedback();
339
469
  },
340
- style: styles2.toggleButtonInRow,
470
+ style: styles3.toggleButtonInRow,
341
471
  accessibilityLabel: `Haptics ${hapticSettings.vibrate ? "on" : "off"}`,
342
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.toggleContent, children: [
343
- /* @__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 }),
344
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
472
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.toggleContent, children: [
473
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
474
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
345
475
  ] })
346
476
  }
347
477
  )
348
478
  ] })
349
479
  ] }),
350
- showAppearance && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.section, children: [
351
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "labelMedium", style: [styles2.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
352
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_auto_paper2.AutoAppearancePicker, { showLabels: false })
480
+ showAppearance && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.section, children: [
481
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "labelMedium", style: [styles3.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
482
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_auto_paper3.AutoAppearancePicker, { showLabels: false })
353
483
  ] }),
354
484
  children,
355
- showUpdateCheck && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles2.section, children: [
356
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_paper2.Text, { variant: "labelSmall", style: [styles2.sectionLabel, { color: colors.onSurfaceVariant }], children: [
485
+ showUpdateCheck && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.section, children: [
486
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native_paper3.Text, { variant: "labelSmall", style: [styles3.sectionLabel, { color: colors.onSurfaceVariant }], children: [
357
487
  "VERSION ",
358
488
  version,
359
489
  updateReady ? " \xB7 UPDATE READY" : ""
360
490
  ] }),
361
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_feedback_press.Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
491
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
362
492
  ] })
363
493
  ] }) })
364
494
  ] }),
365
- 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: [
366
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Icon, { source: "information-outline", size: 64, color: colors.secondary }),
367
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "headlineLarge", style: [styles2.overlayTitle, { color: colors.secondary }], children: infoMessage.title }),
368
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "bodyLarge", style: [styles2.overlayBody, { color: fg }], children: infoMessage.message }),
369
- /* @__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" })
370
- ] }) }) })
495
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ConfirmDialog, { visible: !!updateManifest, title: "Update Available", message: updateManifestDate ? `Released ${updateManifestDate.toLocaleDateString()} at ${updateManifestDate.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation, onConfirm: () => respondToUpdateConfirm(true), onCancel: () => respondToUpdateConfirm(false) }),
496
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ConfirmDialog, { visible: !!infoMessage, title: infoMessage?.title ?? "", message: infoMessage?.message ?? "", confirmLabel: "OK", icon: "information-outline", rotation, onConfirm: () => setInfoMessage(null) })
371
497
  ] });
372
498
  }
373
- var styles2 = import_react_native2.StyleSheet.create({
499
+ var styles3 = import_react_native4.StyleSheet.create({
374
500
  content: {
375
501
  gap: 24,
376
502
  paddingVertical: 20
@@ -392,27 +518,6 @@ var styles2 = import_react_native2.StyleSheet.create({
392
518
  justifyContent: "center",
393
519
  width: 36
394
520
  },
395
- overlay: {
396
- alignItems: "center",
397
- backgroundColor: "rgba(0,0,0,0.72)",
398
- bottom: 0,
399
- justifyContent: "center",
400
- left: 0,
401
- position: "absolute",
402
- right: 0,
403
- top: 0
404
- },
405
- overlayBody: { textAlign: "center" },
406
- overlayButton: { width: 160 },
407
- overlayCard: {
408
- alignItems: "center",
409
- borderRadius: 20,
410
- borderWidth: 1,
411
- gap: 16,
412
- maxWidth: 360,
413
- padding: 32
414
- },
415
- overlayTitle: { fontWeight: "bold" },
416
521
  scrollArea: {
417
522
  marginBottom: 0
418
523
  },
@@ -461,112 +566,25 @@ var styles2 = import_react_native2.StyleSheet.create({
461
566
  // src/BaseStatsScreen.tsx
462
567
  var import_auto_paper4 = require("@rific/auto-paper");
463
568
  var import_feedback_press3 = require("@rific/feedback-press");
464
- var import_core2 = require("@tastic/core");
465
- var import_react2 = require("react");
466
- var import_react_native4 = require("react-native");
569
+ var import_core3 = require("@tastic/core");
570
+ var import_react3 = require("react");
571
+ var import_react_native5 = require("react-native");
467
572
  var import_react_native_paper4 = require("react-native-paper");
468
-
469
- // src/ConfirmDialog.tsx
470
- var import_auto_paper3 = require("@rific/auto-paper");
471
- var import_feedback_press2 = require("@rific/feedback-press");
472
- var import_react_native3 = require("react-native");
473
- var import_react_native_paper3 = require("react-native-paper");
474
- var import_jsx_runtime3 = require("react/jsx-runtime");
475
- function ConfirmDialog({ visible, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, rotation = 0, icon = "help-circle-outline", destructive = false }) {
476
- const { dark, colors } = (0, import_auto_paper3.useAutoPaperTheme)();
477
- if (!visible) return null;
478
- const fg = dark ? "#FFFFFF" : "#000000";
479
- const cardBg = dark ? "#111111" : "#F2F2F2";
480
- const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
481
- const accent = destructive ? colors.danger : colors.primary;
482
- const onAccent = destructive ? colors.onDanger : colors.onPrimary;
483
- return /* @__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, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [styles3.card, { backgroundColor: cardBg, borderColor: cardBorder }, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
484
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Icon, { source: icon, size: 64, color: accent }),
485
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "headlineLarge", style: [styles3.title, { color: accent }], children: title }),
486
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "bodyLarge", style: [styles3.body, { color: fg }], children: message }),
487
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles3.actions, children: [
488
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.Button, { mode: "outlined", onPress: onCancel, style: styles3.actionButton, children: cancelLabel }),
489
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.Button, { mode: "contained", onPress: onConfirm, style: styles3.actionButton, buttonColor: accent, textColor: onAccent, children: confirmLabel })
490
- ] })
491
- ] }) }) });
492
- }
493
- var styles3 = import_react_native3.StyleSheet.create({
494
- // flexBasis (not flex: 1's own 0%) is each button's real floor: whenever both fit side by side,
495
- // flexGrow still splits any leftover width evenly beyond that floor, same visual result as the
496
- // old flex: 1 on a roomy screen - but a button can never be squeezed narrower than 128, only
497
- // wrap to its own row instead (see actions' flexWrap below). react-native-paper's Button
498
- // hardcodes numberOfLines={1} on its label with no override, so a button squeezed under its
499
- // label's natural width silently ellipsizes ("Cancel" -> "Can...") instead of wrapping - assumed
500
- // fixable by just giving the row more width, until confirmed live that these two-word labels
501
- // (Cancel/Reset/New Game) can still lose the truncation fight even with the extra room card's own
502
- // width fix below provides, on a narrow enough screen. flexBasis is the real fix: past a certain
503
- // width there's no side-by-side arrangement that fits both buttons at their natural label size,
504
- // so the layout has to give (stack) rather than the label (truncate).
505
- actionButton: {
506
- flexBasis: 128,
507
- flexGrow: 1
508
- },
509
- actions: {
510
- flexDirection: "row",
511
- flexWrap: "wrap",
512
- gap: 12,
513
- width: "100%"
514
- },
515
- body: {
516
- textAlign: "center"
517
- },
518
- card: {
519
- alignItems: "center",
520
- borderRadius: 20,
521
- borderWidth: 1,
522
- gap: 16,
523
- maxWidth: 360,
524
- padding: 32,
525
- // Claims its full allowance instead of just maxWidth alone, which only caps how wide the card
526
- // is allowed to get - without an explicit width, a flex-centered child sizes to its own content
527
- // instead of stretching, so the card (and therefore actions below) can end up narrower than the
528
- // screen actually has room for, needlessly forcing the action row to wrap sooner than it has to.
529
- width: "100%"
530
- },
531
- overlay: {
532
- alignItems: "center",
533
- backgroundColor: "rgba(0,0,0,0.72)",
534
- bottom: 0,
535
- justifyContent: "center",
536
- left: 0,
537
- // Gives the card room to breathe from the screen edges now that it reliably stretches to fill
538
- // its full width allowance (see card's own width comment) - without this, width: '100%' would
539
- // run the card edge-to-edge on a screen narrower than maxWidth's own 360.
540
- paddingHorizontal: 24,
541
- position: "absolute",
542
- right: 0,
543
- top: 0
544
- },
545
- // textAlign is load-bearing, not decorative, once `title` wraps to more than one line - centered
546
- // within card's own alignItems: 'center' only centers the Text's block as a whole; each wrapped
547
- // line inside that block still defaults to left-aligned without this.
548
- title: {
549
- fontWeight: "bold",
550
- textAlign: "center"
551
- }
552
- });
553
-
554
- // src/BaseStatsScreen.tsx
555
573
  var import_jsx_runtime4 = require("react/jsx-runtime");
556
574
  function BaseStatsScreen({ rotation: rotationOverride, title = "Achievements", titleVariant = "headlineSmall", onBack, insets, onReset, resetLabel = "Reset All Stats", resetConfirmTitle = "Reset Everything?", resetConfirmBody = "This permanently erases all stats and achievements. This cannot be undone.", fg: fgOverride, bg: bgOverride, children }) {
557
575
  const { dark, colors } = (0, import_auto_paper4.useAutoPaperTheme)();
558
- const ambientRotation = (0, import_core2.useRotation)();
576
+ const ambientRotation = (0, import_core3.useRotation)();
559
577
  const rotation = rotationOverride ?? ambientRotation;
560
- const [confirmResetVisible, setConfirmResetVisible] = (0, import_react2.useState)(false);
578
+ const [confirmResetVisible, setConfirmResetVisible] = (0, import_react3.useState)(false);
561
579
  const showReset = !!onReset;
562
580
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
563
581
  const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
564
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: [styles4.container, { backgroundColor: bg }], children: [
565
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: [styles4.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
582
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native5.View, { style: [styles4.container, { backgroundColor: bg }], children: [
583
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native5.View, { style: [styles4.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
566
584
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
567
585
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Text, { variant: titleVariant, style: [styles4.title, { color: fg }], children: title })
568
586
  ] }),
569
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.ScrollView, { style: styles4.scrollView, contentContainerStyle: [styles4.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
587
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native5.ScrollView, { style: styles4.scrollView, contentContainerStyle: [styles4.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
570
588
  children,
571
589
  showReset && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press3.Button, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles4.resetButton, children: resetLabel })
572
590
  ] }),
@@ -590,7 +608,7 @@ function BaseStatsScreen({ rotation: rotationOverride, title = "Achievements", t
590
608
  )
591
609
  ] });
592
610
  }
593
- var styles4 = import_react_native4.StyleSheet.create({
611
+ var styles4 = import_react_native5.StyleSheet.create({
594
612
  container: {
595
613
  flex: 1
596
614
  },
@@ -621,19 +639,19 @@ var styles4 = import_react_native4.StyleSheet.create({
621
639
  });
622
640
 
623
641
  // src/ContentGutter.tsx
624
- var import_core3 = require("@tastic/core");
625
- var import_react_native5 = require("react-native");
642
+ var import_core4 = require("@tastic/core");
643
+ var import_react_native6 = require("react-native");
626
644
  var import_jsx_runtime5 = require("react/jsx-runtime");
627
645
  function ContentGutter({ maxContentWidth, leftGutter, rightGutter, style, children }) {
628
- const { width: windowWidth } = (0, import_react_native5.useWindowDimensions)();
629
- const { contentWidth, gutterWidth } = (0, import_core3.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
630
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: [styles5.row, style], children: [
631
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: [styles5.gutter, { width: gutterWidth }], children: leftGutter }),
632
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: { width: contentWidth }, children }),
633
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: [styles5.gutter, { width: gutterWidth }], children: rightGutter })
646
+ const { width: windowWidth } = (0, import_react_native6.useWindowDimensions)();
647
+ const { contentWidth, gutterWidth } = (0, import_core4.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
648
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native6.View, { style: [styles5.row, style], children: [
649
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native6.View, { style: [styles5.gutter, { width: gutterWidth }], children: leftGutter }),
650
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native6.View, { style: { width: contentWidth }, children }),
651
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native6.View, { style: [styles5.gutter, { width: gutterWidth }], children: rightGutter })
634
652
  ] });
635
653
  }
636
- var styles5 = import_react_native5.StyleSheet.create({
654
+ var styles5 = import_react_native6.StyleSheet.create({
637
655
  // overflow:'hidden' keeps an oversized leftGutter/rightGutter from bleeding into the content
638
656
  // column instead of clipping cleanly at its own real width.
639
657
  gutter: {
@@ -645,22 +663,22 @@ var styles5 = import_react_native5.StyleSheet.create({
645
663
  });
646
664
 
647
665
  // src/ControlSchemePicker.tsx
648
- var import_core6 = require("@tastic/core");
649
- var import_react_native11 = require("react-native");
666
+ var import_core7 = require("@tastic/core");
667
+ var import_react_native12 = require("react-native");
650
668
 
651
669
  // src/SectionedDropdown.tsx
652
670
  var import_auto_paper5 = require("@rific/auto-paper");
653
671
  var import_feedback_press4 = require("@rific/feedback-press");
654
- var import_core5 = require("@tastic/core");
655
- var import_react_native10 = require("react-native");
672
+ var import_core6 = require("@tastic/core");
673
+ var import_react_native11 = require("react-native");
656
674
  var import_react_native_paper5 = require("react-native-paper");
657
675
 
658
676
  // src/fonts.ts
659
- var import_react_native6 = require("react-native");
660
- var MONO_FONT = import_react_native6.Platform.select({ ios: "Menlo", android: "monospace", default: "monospace" });
677
+ var import_react_native7 = require("react-native");
678
+ var MONO_FONT = import_react_native7.Platform.select({ ios: "Menlo", android: "monospace", default: "monospace" });
661
679
 
662
680
  // src/PopoverBody.tsx
663
- var import_react_native7 = require("react-native");
681
+ var import_react_native8 = require("react-native");
664
682
  var import_jsx_runtime6 = require("react/jsx-runtime");
665
683
  var CARET_SIZE = 7;
666
684
  var CARET_RING_OFFSET = 2;
@@ -675,15 +693,15 @@ function PopoverBody({ visible, children, align = "center", verticalAlign = "bel
675
693
  const caretFill = above ? "borderTopColor" : "borderBottomColor";
676
694
  const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
677
695
  const caretEdge = (size) => above ? { bottom: -size + CARET_DIP } : { top: -size + CARET_DIP };
678
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native7.View, { style: [styles6.content, alignStyle, verticalStyle], children: [
696
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native8.View, { style: [styles6.content, alignStyle, verticalStyle], children: [
679
697
  children,
680
698
  caretColor && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
681
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native7.View, { style: [styles6.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
682
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native7.View, { style: [styles6.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
699
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native8.View, { style: [styles6.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
700
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native8.View, { style: [styles6.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
683
701
  ] })
684
702
  ] });
685
703
  }
686
- var styles6 = import_react_native7.StyleSheet.create({
704
+ var styles6 = import_react_native8.StyleSheet.create({
687
705
  caret: {
688
706
  borderLeftColor: "transparent",
689
707
  borderRightColor: "transparent",
@@ -726,11 +744,11 @@ var styles6 = import_react_native7.StyleSheet.create({
726
744
  });
727
745
 
728
746
  // src/SkiaGate.tsx
729
- var import_react3 = require("react");
747
+ var import_react4 = require("react");
730
748
  var import_jsx_runtime7 = require("react/jsx-runtime");
731
749
  function SkiaGate({ getComponent, componentProps, fallback = null }) {
732
- const [Component, setComponent] = (0, import_react3.useState)(null);
733
- (0, import_react3.useEffect)(() => {
750
+ const [Component, setComponent] = (0, import_react4.useState)(null);
751
+ (0, import_react4.useEffect)(() => {
734
752
  let cancelled = false;
735
753
  getComponent().then((mod) => {
736
754
  if (!cancelled) setComponent(() => mod.default);
@@ -751,8 +769,8 @@ function TriggerGaugeHost(props) {
751
769
  }
752
770
 
753
771
  // src/useAutoAlign.ts
754
- var import_react5 = require("react");
755
- var import_react_native9 = require("react-native");
772
+ var import_react6 = require("react");
773
+ var import_react_native10 = require("react-native");
756
774
  var EDGE_MARGIN = 12;
757
775
  function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
758
776
  if (rotation === 0) return { x, y, width, height };
@@ -764,18 +782,18 @@ function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
764
782
  return { x: cx + newDx - newWidth / 2, y: cy + newDy - newHeight / 2, width: newWidth, height: newHeight };
765
783
  }
766
784
  function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
767
- const triggerRef = (0, import_react5.useRef)(null);
768
- const { width: windowWidth, height: windowHeight } = (0, import_react_native9.useWindowDimensions)();
769
- const [align, setAlign] = (0, import_react5.useState)("center");
770
- const [verticalAlign, setVerticalAlign] = (0, import_react5.useState)("below");
771
- const [maxHeight, setMaxHeight] = (0, import_react5.useState)(contentHeight);
772
- const [measured, setMeasured] = (0, import_react5.useState)(false);
773
- const [prevOpen, setPrevOpen] = (0, import_react5.useState)(open);
785
+ const triggerRef = (0, import_react6.useRef)(null);
786
+ const { width: windowWidth, height: windowHeight } = (0, import_react_native10.useWindowDimensions)();
787
+ const [align, setAlign] = (0, import_react6.useState)("center");
788
+ const [verticalAlign, setVerticalAlign] = (0, import_react6.useState)("below");
789
+ const [maxHeight, setMaxHeight] = (0, import_react6.useState)(contentHeight);
790
+ const [measured, setMeasured] = (0, import_react6.useState)(false);
791
+ const [prevOpen, setPrevOpen] = (0, import_react6.useState)(open);
774
792
  if (open !== prevOpen) {
775
793
  setPrevOpen(open);
776
794
  if (open) setMeasured(false);
777
795
  }
778
- const measure = (0, import_react5.useCallback)(() => {
796
+ const measure = (0, import_react6.useCallback)(() => {
779
797
  triggerRef.current?.measureInWindow((rawX, rawY, rawWidth, rawHeight) => {
780
798
  const { x, y, width: triggerWidth, height: triggerHeight } = rotateRect(rawX, rawY, rawWidth, rawHeight, rotation, windowWidth, windowHeight);
781
799
  const centerX = x + triggerWidth / 2;
@@ -792,7 +810,7 @@ function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
792
810
  setMeasured(true);
793
811
  });
794
812
  }, [contentWidth, contentHeight, windowWidth, windowHeight, rotation]);
795
- (0, import_react5.useEffect)(() => {
813
+ (0, import_react6.useEffect)(() => {
796
814
  if (open) measure();
797
815
  }, [open, measure]);
798
816
  return { align, maxHeight, measured, triggerRef, verticalAlign };
@@ -811,7 +829,7 @@ var MENU_ITEM_HEIGHT_WITH_DESCRIPTION = 58;
811
829
  var MENU_DIVIDER_HEIGHT = 9;
812
830
  var MENU_VERTICAL_PADDING = 12;
813
831
  function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accentColor, mutedColor, onAccentColor, dark, align: alignOverride, rotation: rotationOverride, autoDismiss = true, labelFontFamily = MONO_FONT, allClearLabels = { all: "All", clear: "Clear" } }) {
814
- const ambientRotation = (0, import_core5.useRotation)();
832
+ const ambientRotation = (0, import_core6.useRotation)();
815
833
  const rotation = rotationOverride ?? ambientRotation;
816
834
  const menuBg = dark ? "#000000" : "#FFFFFF";
817
835
  const menuBorderColor = (0, import_auto_paper5.getBlendedColor)(mutedColor, menuBg, 0.5);
@@ -850,13 +868,13 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
850
868
  const anchorStyle = [styles8.anchor, open && styles8.anchorOpen];
851
869
  const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
852
870
  const dividerStyle = { backgroundColor: menuBorderColor };
853
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { style: anchorStyle, children: [
854
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { ref: triggerRef, collapsable: false, style: styles8.triggerBox, children: [
871
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: anchorStyle, children: [
872
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { ref: triggerRef, collapsable: false, style: styles8.triggerBox, children: [
855
873
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
856
874
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press4.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
857
875
  ] }),
858
- /* @__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_native10.ScrollView, { style: [styles8.menu, menuStyle], contentContainerStyle: styles8.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { children: [
859
- sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native10.View, { style: [styles8.divider, dividerStyle] }),
876
+ /* @__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: [
877
+ sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native11.View, { style: [styles8.divider, dividerStyle] }),
860
878
  section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
861
879
  SingleSection,
862
880
  {
@@ -889,9 +907,9 @@ function SingleSection({
889
907
  const itemStyle = [styles8.item, selected && { backgroundColor: accentColor }, taken && styles8.itemTaken];
890
908
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
891
909
  const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
892
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press4.TouchableRipple, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { style: styles8.itemRow, children: [
910
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press4.TouchableRipple, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: styles8.itemRow, children: [
893
911
  option.icon && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
894
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { style: styles8.itemLabelColumn, children: [
912
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: styles8.itemLabelColumn, children: [
895
913
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
896
914
  option.description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
897
915
  ] })
@@ -915,7 +933,7 @@ function MultiSection({
915
933
  const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
916
934
  const itemStyle = [styles8.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
917
935
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
918
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native10.View, { style: styles8.itemRow, children: [
936
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: styles8.itemRow, children: [
919
937
  option.icon && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
920
938
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper5.Text, { variant: "labelLarge", style: [styles8.itemLabelFlex, labelStyle], children: option.label })
921
939
  ] }) }, option.value);
@@ -923,7 +941,7 @@ function MultiSection({
923
941
  section.allClear && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press4.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 }) })
924
942
  ] });
925
943
  }
926
- var styles8 = import_react_native10.StyleSheet.create({
944
+ var styles8 = import_react_native11.StyleSheet.create({
927
945
  allClearLabel: {
928
946
  fontWeight: "bold",
929
947
  textAlign: "center"
@@ -940,7 +958,7 @@ var styles8 = import_react_native10.StyleSheet.create({
940
958
  zIndex: 100
941
959
  },
942
960
  divider: {
943
- height: import_react_native10.StyleSheet.hairlineWidth,
961
+ height: import_react_native11.StyleSheet.hairlineWidth,
944
962
  marginVertical: 4
945
963
  },
946
964
  item: {
@@ -995,14 +1013,14 @@ var styles8 = import_react_native10.StyleSheet.create({
995
1013
  // src/ControlSchemePicker.tsx
996
1014
  var import_jsx_runtime11 = require("react/jsx-runtime");
997
1015
  function ControlSchemePicker({ id, host, value, onChange, options, takenValue, accentColor, mutedColor, onAccentColor, dark, align, labelFontFamily }) {
998
- const isTouchPrimary = (0, import_core6.useIsTouchPrimaryDevice)();
999
- if (import_react_native11.Platform.OS !== "web" || isTouchPrimary) return null;
1016
+ const isTouchPrimary = (0, import_core7.useIsTouchPrimaryDevice)();
1017
+ if (import_react_native12.Platform.OS !== "web" || isTouchPrimary) return null;
1000
1018
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SectionedDropdown, { id, host, icon: "keyboard-outline", accessibilityLabel: "Control scheme", sections: [{ kind: "single", id: "controls", options, value, onChange, takenValue }], accentColor, mutedColor, onAccentColor, dark, align, labelFontFamily });
1001
1019
  }
1002
1020
 
1003
1021
  // src/CornerActionButtons.tsx
1004
1022
  var import_feedback_press5 = require("@rific/feedback-press");
1005
- var import_react_native12 = require("react-native");
1023
+ var import_react_native13 = require("react-native");
1006
1024
  var import_jsx_runtime12 = require("react/jsx-runtime");
1007
1025
  function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon = "arrow-left", backLabel = "Back" }) {
1008
1026
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
@@ -1010,7 +1028,7 @@ function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon = "arrow
1010
1028
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_feedback_press5.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles9.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
1011
1029
  ] });
1012
1030
  }
1013
- var styles9 = import_react_native12.StyleSheet.create({
1031
+ var styles9 = import_react_native13.StyleSheet.create({
1014
1032
  back: {
1015
1033
  position: "absolute"
1016
1034
  },
@@ -1020,22 +1038,22 @@ var styles9 = import_react_native12.StyleSheet.create({
1020
1038
  });
1021
1039
 
1022
1040
  // src/CornerStatusBadges.tsx
1023
- var import_react_native13 = require("react-native");
1041
+ var import_react_native14 = require("react-native");
1024
1042
  var import_react_native_paper6 = require("react-native-paper");
1025
1043
  var import_jsx_runtime13 = require("react/jsx-runtime");
1026
1044
  var BADGE_SIZE = 30;
1027
1045
  function StatusBadge({ icon }) {
1028
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native13.View, { style: styles10.badge, children: icon && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper6.Icon, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
1046
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native14.View, { style: styles10.badge, children: icon && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper6.Icon, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
1029
1047
  }
1030
1048
  function CornerStatusBadges({ orientationMode, p1OnRight, p1, p2 }) {
1031
1049
  const p1OnRightSide = orientationMode === "sideBySide" && p1OnRight;
1032
1050
  const p2OnLeftSide = orientationMode === "sideBySide" && !p1OnRight;
1033
1051
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1034
- p1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native13.View, { pointerEvents: "none", style: p1OnRightSide ? styles10.bottomRight : styles10.bottomLeft, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatusBadge, { icon: p1.icon }) }),
1035
- p2 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native13.View, { pointerEvents: "none", style: p2OnLeftSide ? styles10.topLeft : styles10.topRight, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatusBadge, { icon: p2.icon }) })
1052
+ p1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native14.View, { pointerEvents: "none", style: p1OnRightSide ? styles10.bottomRight : styles10.bottomLeft, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatusBadge, { icon: p1.icon }) }),
1053
+ p2 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native14.View, { pointerEvents: "none", style: p2OnLeftSide ? styles10.topLeft : styles10.topRight, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatusBadge, { icon: p2.icon }) })
1036
1054
  ] });
1037
1055
  }
1038
- var styles10 = import_react_native13.StyleSheet.create({
1056
+ var styles10 = import_react_native14.StyleSheet.create({
1039
1057
  badge: {
1040
1058
  alignItems: "center",
1041
1059
  backgroundColor: "rgba(0,0,0,0.28)",
@@ -1056,8 +1074,8 @@ var styles10 = import_react_native13.StyleSheet.create({
1056
1074
  // src/InlineColorPicker.tsx
1057
1075
  var import_auto_paper6 = require("@rific/auto-paper");
1058
1076
  var import_feedback_press6 = require("@rific/feedback-press");
1059
- var import_core7 = require("@tastic/core");
1060
- var import_react_native14 = require("react-native");
1077
+ var import_core8 = require("@tastic/core");
1078
+ var import_react_native15 = require("react-native");
1061
1079
  var import_react_native_paper7 = require("react-native-paper");
1062
1080
  var import_jsx_runtime14 = require("react/jsx-runtime");
1063
1081
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
@@ -1073,11 +1091,11 @@ function widthForColumns(columns) {
1073
1091
  return SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * columns + SWATCHES_GAP * (columns - 1);
1074
1092
  }
1075
1093
  function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = import_auto_paper6.defaultColors, takenValue, allowSwapTaken, dark, align: alignOverride, rotation: rotationOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
1076
- const ambientRotation = (0, import_core7.useRotation)();
1094
+ const ambientRotation = (0, import_core8.useRotation)();
1077
1095
  const rotation = rotationOverride ?? ambientRotation;
1078
1096
  const menuBg = dark ? "#000000" : "#FFFFFF";
1079
- const { width: windowWidth } = (0, import_react_native14.useWindowDimensions)();
1080
- const autoColumns = (0, import_core7.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
1097
+ const { width: windowWidth } = (0, import_react_native15.useWindowDimensions)();
1098
+ const autoColumns = (0, import_core8.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
1081
1099
  const resolvedColumns = columns ?? autoColumns;
1082
1100
  const swatchesWidth = widthForColumns(resolvedColumns);
1083
1101
  const swatchRows = Math.ceil(swatches.length / resolvedColumns);
@@ -1089,9 +1107,9 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1089
1107
  const displayValue = displayFor(value);
1090
1108
  const contrastColor = (0, import_auto_paper6.getContrastColor)(displayValue);
1091
1109
  const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
1092
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native14.View, { style: [styles11.anchor, open && styles11.anchorOpen], children: [
1093
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press6.TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles11.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native14.View, { ref: triggerRef, collapsable: false, style: styles11.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Text, { style: [styles11.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
1094
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native14.ScrollView, { style: [styles11.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles11.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
1110
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native15.View, { style: [styles11.anchor, open && styles11.anchorOpen], children: [
1111
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press6.TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles11.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native15.View, { ref: triggerRef, collapsable: false, style: styles11.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Text, { style: [styles11.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
1112
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native15.ScrollView, { style: [styles11.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles11.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
1095
1113
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
1096
1114
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
1097
1115
  const swappable = taken && allowSwapTaken;
@@ -1106,14 +1124,14 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1106
1124
  },
1107
1125
  borderless: true,
1108
1126
  style: [styles11.swatch, { backgroundColor: swatchDisplay }, selected && styles11.swatchSelected, taken && !swappable && styles11.swatchTaken],
1109
- children: selected ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: "check", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: "close", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native14.View, {})
1127
+ children: selected ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: "check", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper7.Icon, { source: "close", size: 16, color: (0, import_auto_paper6.getContrastColor)(swatchDisplay) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native15.View, {})
1110
1128
  },
1111
1129
  swatch.value
1112
1130
  );
1113
1131
  }) }) })
1114
1132
  ] });
1115
1133
  }
1116
- var styles11 = import_react_native14.StyleSheet.create({
1134
+ var styles11 = import_react_native15.StyleSheet.create({
1117
1135
  anchor: {
1118
1136
  position: "relative"
1119
1137
  },
@@ -1187,7 +1205,7 @@ var styles11 = import_react_native14.StyleSheet.create({
1187
1205
  // src/LabeledDropdown.tsx
1188
1206
  var import_auto_paper7 = require("@rific/auto-paper");
1189
1207
  var import_feedback_press7 = require("@rific/feedback-press");
1190
- var import_react_native15 = require("react-native");
1208
+ var import_react_native16 = require("react-native");
1191
1209
  var import_react_native_paper8 = require("react-native-paper");
1192
1210
  var import_jsx_runtime15 = require("react/jsx-runtime");
1193
1211
  var TRIGGER_HEIGHT = 28;
@@ -1212,22 +1230,22 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
1212
1230
  onChange(option.value);
1213
1231
  host.close();
1214
1232
  };
1215
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native15.View, { style: [styles12.anchor, open && styles12.anchorOpen], children: [
1216
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => host.toggle(id), style: styles12.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native15.View, { ref: triggerRef, collapsable: false, style: styles12.triggerInner, children: [
1233
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { style: [styles12.anchor, open && styles12.anchorOpen], children: [
1234
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => host.toggle(id), style: styles12.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { ref: triggerRef, collapsable: false, style: styles12.triggerInner, children: [
1217
1235
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper8.Text, { style: [styles12.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
1218
1236
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper8.Icon, { source: "menu-down", size: 16, color })
1219
1237
  ] }) }),
1220
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native15.ScrollView, { style: [styles12.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles12.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
1238
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native16.ScrollView, { style: [styles12.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles12.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
1221
1239
  const isSelected = option.value === value;
1222
1240
  const onColor = (0, import_auto_paper7.getContrastColor)(color);
1223
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => handleSelect(option), style: [styles12.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native15.View, { style: styles12.rowInner, children: [
1241
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => handleSelect(option), style: [styles12.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { style: styles12.rowInner, children: [
1224
1242
  option.icon && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper8.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
1225
1243
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper8.Text, { style: [styles12.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
1226
1244
  ] }) }, option.value);
1227
1245
  }) }) })
1228
1246
  ] });
1229
1247
  }
1230
- var styles12 = import_react_native15.StyleSheet.create({
1248
+ var styles12 = import_react_native16.StyleSheet.create({
1231
1249
  anchor: {
1232
1250
  position: "relative"
1233
1251
  },
@@ -1285,16 +1303,16 @@ function loadSkiaWeb() {
1285
1303
  }
1286
1304
 
1287
1305
  // src/PressAwayOverlay.tsx
1288
- var import_react_native16 = require("react-native");
1306
+ var import_react_native17 = require("react-native");
1289
1307
  var import_jsx_runtime16 = require("react/jsx-runtime");
1290
1308
  function PressAwayOverlay({ active, onPress, style }) {
1291
1309
  if (!active) return null;
1292
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native16.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native16.StyleSheet.absoluteFill, style] });
1310
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native17.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native17.StyleSheet.absoluteFill, style] });
1293
1311
  }
1294
1312
 
1295
1313
  // src/ReadyButton.tsx
1296
1314
  var import_feedback_press8 = require("@rific/feedback-press");
1297
- var import_react_native17 = require("react-native");
1315
+ var import_react_native18 = require("react-native");
1298
1316
  var import_react_native_paper9 = require("react-native-paper");
1299
1317
  var import_jsx_runtime17 = require("react/jsx-runtime");
1300
1318
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
@@ -1303,7 +1321,7 @@ function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MON
1303
1321
  const labelColorStyle = { color: ready ? "#000000" : color };
1304
1322
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_feedback_press8.TouchableRipple, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native_paper9.Text, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
1305
1323
  }
1306
- var styles13 = import_react_native17.StyleSheet.create({
1324
+ var styles13 = import_react_native18.StyleSheet.create({
1307
1325
  readyButton: {
1308
1326
  alignItems: "center",
1309
1327
  borderRadius: 12,
@@ -1317,11 +1335,11 @@ var styles13 = import_react_native17.StyleSheet.create({
1317
1335
 
1318
1336
  // src/SharedActionBand.tsx
1319
1337
  var import_feedback_press9 = require("@rific/feedback-press");
1320
- var import_react_native18 = require("react-native");
1338
+ var import_react_native19 = require("react-native");
1321
1339
  var import_jsx_runtime18 = require("react/jsx-runtime");
1322
1340
  function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
1323
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native18.View, { style: [styles14.column, popoverOpen && styles14.columnOpen], children: [
1324
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native18.View, { style: styles14.actionsRow, children: [
1341
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native19.View, { style: [styles14.column, popoverOpen && styles14.columnOpen], children: [
1342
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native19.View, { style: styles14.actionsRow, children: [
1325
1343
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press9.IconButton, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1326
1344
  onRandomize && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press9.IconButton, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1327
1345
  onReset && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press9.IconButton, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
@@ -1330,7 +1348,7 @@ function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popove
1330
1348
  children
1331
1349
  ] });
1332
1350
  }
1333
- var styles14 = import_react_native18.StyleSheet.create({
1351
+ var styles14 = import_react_native19.StyleSheet.create({
1334
1352
  actionsRow: {
1335
1353
  alignItems: "center",
1336
1354
  flexDirection: "row",
@@ -1347,19 +1365,19 @@ var styles14 = import_react_native18.StyleSheet.create({
1347
1365
 
1348
1366
  // src/StatRow.tsx
1349
1367
  var import_auto_paper8 = require("@rific/auto-paper");
1350
- var import_react_native19 = require("react-native");
1368
+ var import_react_native20 = require("react-native");
1351
1369
  var import_react_native_paper10 = require("react-native-paper");
1352
1370
  var import_jsx_runtime19 = require("react/jsx-runtime");
1353
1371
  function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }) {
1354
1372
  const { dark } = (0, import_auto_paper8.useAutoPaperTheme)();
1355
1373
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
1356
1374
  const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
1357
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native19.View, { style: styles15.row, children: [
1375
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native20.View, { style: styles15.row, children: [
1358
1376
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper10.Text, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
1359
1377
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper10.Text, { variant: "bodyMedium", style: [styles15.value, { color: fg }], children: value })
1360
1378
  ] });
1361
1379
  }
1362
- var styles15 = import_react_native19.StyleSheet.create({
1380
+ var styles15 = import_react_native20.StyleSheet.create({
1363
1381
  row: {
1364
1382
  alignItems: "center",
1365
1383
  flexDirection: "row",
@@ -1372,19 +1390,19 @@ var styles15 = import_react_native19.StyleSheet.create({
1372
1390
 
1373
1391
  // src/StatSection.tsx
1374
1392
  var import_auto_paper9 = require("@rific/auto-paper");
1375
- var import_react_native20 = require("react-native");
1393
+ var import_react_native21 = require("react-native");
1376
1394
  var import_react_native_paper11 = require("react-native-paper");
1377
1395
  var import_jsx_runtime20 = require("react/jsx-runtime");
1378
1396
  function StatSection({ label, fg: fgOverride, sectionBg: sectionBgOverride, children }) {
1379
1397
  const { dark } = (0, import_auto_paper9.useAutoPaperTheme)();
1380
1398
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
1381
1399
  const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
1382
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native20.View, { style: [styles16.section, { backgroundColor: sectionBg }], children: [
1400
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native21.View, { style: [styles16.section, { backgroundColor: sectionBg }], children: [
1383
1401
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper11.Text, { variant: "labelMedium", style: [styles16.label, { color: fg }], children: label }),
1384
1402
  children
1385
1403
  ] });
1386
1404
  }
1387
- var styles16 = import_react_native20.StyleSheet.create({
1405
+ var styles16 = import_react_native21.StyleSheet.create({
1388
1406
  // Bold and full-contrast (fg), not the original fontless/fgMuted treatment — that read at the
1389
1407
  // same visual weight as StatRow's own row labels below it (both used the identical muted color;
1390
1408
  // letterSpacing/variant alone didn't separate them enough to read as "this is the group header"
@@ -1400,14 +1418,24 @@ var styles16 = import_react_native20.StyleSheet.create({
1400
1418
  }
1401
1419
  });
1402
1420
 
1421
+ // src/UpdateDialog.tsx
1422
+ var import_updater2 = require("@rific/updater");
1423
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1424
+ function UpdateDialog({ rotation = 0, autoCheck = true, autoPrompt = true, onError }) {
1425
+ const { manifest, onConfirm, respond } = useUpdateConfirmBridge();
1426
+ (0, import_updater2.useUpdater)({ autoCheck, autoPrompt, onConfirm, onError });
1427
+ const date = manifest ? new Date(manifest.createdAt) : null;
1428
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ConfirmDialog, { visible: !!manifest, title: "Update Available", message: date ? `Released ${date.toLocaleDateString()} at ${date.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation, onConfirm: () => respond(true), onCancel: () => respond(false) });
1429
+ }
1430
+
1403
1431
  // src/usePopoverHost.ts
1404
- var import_react6 = require("react");
1432
+ var import_react7 = require("react");
1405
1433
  function usePopoverHost() {
1406
- const [openId, setOpenId] = (0, import_react6.useState)(null);
1407
- const toggle = (0, import_react6.useCallback)((id) => {
1434
+ const [openId, setOpenId] = (0, import_react7.useState)(null);
1435
+ const toggle = (0, import_react7.useCallback)((id) => {
1408
1436
  setOpenId((prev) => prev === id ? null : id);
1409
1437
  }, []);
1410
- const close = (0, import_react6.useCallback)(() => setOpenId(null), []);
1438
+ const close = (0, import_react7.useCallback)(() => setOpenId(null), []);
1411
1439
  return { openId, toggle, close };
1412
1440
  }
1413
1441
  // Annotate the CommonJS export names for ESM import in node:
@@ -1434,8 +1462,11 @@ function usePopoverHost() {
1434
1462
  StatRow,
1435
1463
  StatSection,
1436
1464
  TriggerGaugeHost,
1465
+ UpdateDialog,
1437
1466
  getLabeledDropdownContentHeight,
1467
+ getOverlayCardColors,
1438
1468
  loadSkiaWeb,
1469
+ overlayStyles,
1439
1470
  useAutoAlign,
1440
1471
  usePopoverHost
1441
1472
  });