@tastic/hud 0.16.0 → 0.18.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.
@@ -35,20 +35,26 @@ __export(chrome_exports, {
35
35
  ActivityStatSection: () => ActivityStatSection,
36
36
  BaseSettingsDialog: () => BaseSettingsDialog,
37
37
  BaseStatsScreen: () => BaseStatsScreen,
38
+ COMPACT_DIALOG_HEIGHT: () => COMPACT_DIALOG_HEIGHT,
38
39
  ConfirmDialog: () => ConfirmDialog,
39
40
  ContentGutter: () => ContentGutter,
40
41
  CornerActionButtons: () => CornerActionButtons,
42
+ DialogHeader: () => DialogHeader,
41
43
  LOCKED_BADGE_COLOR: () => LOCKED_BADGE_COLOR,
42
44
  MONO_FONT: () => MONO_FONT,
45
+ OverlayBackdrop: () => OverlayBackdrop,
46
+ OverlayCard: () => OverlayCard,
43
47
  RotatedFrame: () => RotatedFrame,
44
48
  StaggeredWord: () => StaggeredWord,
45
49
  StatRow: () => StatRow,
46
50
  StatSection: () => StatSection,
47
51
  UpdateDialog: () => UpdateDialog,
48
- getOverlayCardColors: () => getOverlayCardColors,
49
52
  getStaggeredWordDuration: () => getStaggeredWordDuration,
53
+ isCompactDialogHeight: () => isCompactDialogHeight,
50
54
  overlayActionStyles: () => overlayActionStyles,
51
- overlayStyles: () => overlayStyles
55
+ overlayStyles: () => overlayStyles,
56
+ useCompactDialog: () => useCompactDialog,
57
+ useOverlayCardColors: () => useOverlayCardColors
52
58
  });
53
59
  module.exports = __toCommonJS(chrome_exports);
54
60
 
@@ -213,39 +219,74 @@ function ActivityStatSection({ stats }) {
213
219
  }
214
220
 
215
221
  // src/BaseSettingsDialog.tsx
216
- var import_auto_paper6 = require("@rific/auto-paper");
222
+ var import_auto_paper8 = require("@rific/auto-paper");
217
223
  var import_feedback_press2 = require("@rific/feedback-press");
218
224
  var import_updater = require("@rific/updater");
219
- var import_core3 = require("@tastic/core");
220
- var import_react4 = require("react");
221
- var import_react_native9 = require("react-native");
222
- var import_react_native_paper7 = require("react-native-paper");
225
+ var import_core4 = require("@tastic/core");
226
+ var import_react5 = require("react");
227
+ var import_react_native12 = require("react-native");
228
+ var import_react_native_paper8 = require("react-native-paper");
229
+ var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
223
230
 
224
231
  // src/ConfirmDialog.tsx
225
- var import_auto_paper5 = require("@rific/auto-paper");
232
+ var import_auto_paper7 = require("@rific/auto-paper");
226
233
  var import_feedback_press = require("@rific/feedback-press");
227
- var import_core = require("@tastic/core");
228
- var import_react_native7 = require("react-native");
229
- var import_react_native_paper5 = require("react-native-paper");
234
+ var import_core2 = require("@tastic/core");
235
+ var import_react_native10 = require("react-native");
236
+ var import_react_native_paper6 = require("react-native-paper");
230
237
 
231
- // src/overlayCard.ts
238
+ // src/DialogHeader.tsx
239
+ var import_auto_paper5 = require("@rific/auto-paper");
232
240
  var import_react_native6 = require("react-native");
233
- function getOverlayCardColors(dark) {
234
- return {
235
- fg: dark ? "#FFFFFF" : "#000000",
236
- fgMuted: dark ? "rgba(255,255,255,0.4)" : "rgba(0,0,0,0.4)",
237
- cardBg: dark ? "#111111" : "#F2F2F2",
238
- cardBorder: dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)"
239
- };
241
+ var import_react_native_paper5 = require("react-native-paper");
242
+ var import_jsx_runtime6 = require("react/jsx-runtime");
243
+ var COMPACT_DIALOG_HEIGHT = 528;
244
+ function isCompactDialogHeight(height) {
245
+ return height < COMPACT_DIALOG_HEIGHT;
246
+ }
247
+ function DialogHeader({ icon, title, color, compact = false, style }) {
248
+ const { colors } = (0, import_auto_paper5.useAutoPaperTheme)();
249
+ const accent = color ?? colors.primary;
250
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native6.View, { style: [compact ? styles5.compact : styles5.full, style], children: [
251
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper5.Icon, { source: icon, size: compact ? COMPACT_ICON_SIZE : ICON_SIZE, color: accent }),
252
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper5.Text, { accessibilityRole: "header", variant: compact ? "headlineSmall" : "headlineLarge", style: [styles5.title, compact && styles5.titleCompact, { color: accent }], children: title })
253
+ ] });
240
254
  }
241
- var overlayStyles = import_react_native6.StyleSheet.create({
255
+ var ICON_SIZE = 64;
256
+ var COMPACT_ICON_SIZE = 32;
257
+ var styles5 = import_react_native6.StyleSheet.create({
258
+ compact: {
259
+ alignItems: "center",
260
+ alignSelf: "stretch",
261
+ flexDirection: "row",
262
+ gap: 12,
263
+ justifyContent: "center"
264
+ },
265
+ full: {
266
+ alignItems: "center",
267
+ alignSelf: "stretch",
268
+ gap: 16
269
+ },
270
+ // textAlign matters once a title wraps: the card centers the Text's block, but each wrapped line
271
+ // still defaults to left-aligned without it.
272
+ title: {
273
+ fontWeight: "bold",
274
+ textAlign: "center"
275
+ },
276
+ titleCompact: {
277
+ flexShrink: 1
278
+ }
279
+ });
280
+
281
+ // src/overlayCard.ts
282
+ var import_react_native7 = require("react-native");
283
+ var overlayStyles = import_react_native7.StyleSheet.create({
242
284
  card: {
243
285
  alignItems: "center",
244
- borderRadius: 20,
245
- borderWidth: 1,
286
+ borderRadius: 28,
246
287
  gap: 16,
247
288
  maxWidth: 360,
248
- padding: 32
289
+ padding: 24
249
290
  },
250
291
  overlay: {
251
292
  alignItems: "center",
@@ -258,7 +299,7 @@ var overlayStyles = import_react_native6.StyleSheet.create({
258
299
  top: 0
259
300
  }
260
301
  });
261
- var overlayActionStyles = import_react_native6.StyleSheet.create({
302
+ var overlayActionStyles = import_react_native7.StyleSheet.create({
262
303
  // minWidth (not flexBasis) is each button's real floor - whenever both fit side by side, flexGrow
263
304
  // still splits any leftover width evenly beyond that floor, same visual result as a plain flex: 1
264
305
  // on a roomy screen, but a button can never be squeezed narrower than 128, only wrap to its own
@@ -300,26 +341,64 @@ var overlayActionStyles = import_react_native6.StyleSheet.create({
300
341
  }
301
342
  });
302
343
 
344
+ // src/OverlaySurface.tsx
345
+ var import_auto_paper6 = require("@rific/auto-paper");
346
+ var import_react_native8 = require("react-native");
347
+ var import_jsx_runtime7 = require("react/jsx-runtime");
348
+ function useOverlayCardColors() {
349
+ const { colors } = (0, import_auto_paper6.useAutoPaperTheme)();
350
+ return { fg: colors.onSurface, fgMuted: colors.onSurfaceVariant, cardBorder: colors.outlineVariant, backdrop: colors.backdrop };
351
+ }
352
+ function OverlayBackdrop({ children, style, onLayout }) {
353
+ const { backdrop } = useOverlayCardColors();
354
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native8.View, { onLayout, style: [overlayStyles.overlay, { backgroundColor: backdrop }, style], children });
355
+ }
356
+ function OverlayCard({ children, style, ...rest }) {
357
+ const { isV3, roundness } = (0, import_auto_paper6.useAutoPaperTheme)();
358
+ const blur = (0, import_auto_paper6.useBlur)();
359
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_auto_paper6.BlurView, { blur, style: [overlayStyles.card, styles6.card, { borderRadius: (isV3 ? 7 : 1) * roundness }, style], ...rest, children });
360
+ }
361
+ var styles6 = import_react_native8.StyleSheet.create({
362
+ card: { overflow: "hidden" }
363
+ });
364
+
365
+ // src/useCompactDialog.ts
366
+ var import_core = require("@tastic/core");
367
+ var import_react2 = require("react");
368
+ var import_react_native9 = require("react-native");
369
+ function useCompactDialog(rotation = 0) {
370
+ const window = (0, import_react_native9.useWindowDimensions)();
371
+ const framed = (0, import_core.useRotatedWindowDimensions)();
372
+ const [measured, setMeasured] = (0, import_react2.useState)(null);
373
+ const onLayout = (0, import_react2.useCallback)((e) => {
374
+ const { width, height } = e.nativeEvent.layout;
375
+ setMeasured((prev) => prev && prev.width === width && prev.height === height ? prev : { width, height });
376
+ }, []);
377
+ const box = measured ?? (rotation === 0 ? framed : window);
378
+ const turned = rotation === 90 || rotation === -90;
379
+ return { compact: isCompactDialogHeight(turned ? box.width : box.height), onLayout };
380
+ }
381
+
303
382
  // src/ConfirmDialog.tsx
304
- var import_jsx_runtime6 = require("react/jsx-runtime");
383
+ var import_jsx_runtime8 = require("react/jsx-runtime");
305
384
  function ConfirmDialog({ visible, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, rotation = 0, icon = "help-circle-outline", destructive = false }) {
306
- const { dark, colors } = (0, import_auto_paper5.useAutoPaperTheme)();
385
+ const { colors } = (0, import_auto_paper7.useAutoPaperTheme)();
386
+ const { fg } = useOverlayCardColors();
387
+ const { compact, onLayout } = useCompactDialog(rotation);
307
388
  if (!visible) return null;
308
- const { fg, cardBg, cardBorder } = getOverlayCardColors(dark);
309
389
  const accent = destructive ? colors.danger : colors.primary;
310
390
  const onAccent = destructive ? colors.onDanger : colors.onPrimary;
311
391
  const showCancel = cancelLabel !== void 0 && onCancel !== void 0;
312
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper5.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native7.View, { style: [overlayStyles.overlay, styles5.overlay], children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native7.View, { style: [overlayStyles.card, styles5.card, { backgroundColor: cardBg, borderColor: cardBorder }, (0, import_core.toRotationStyle)(rotation)], children: [
313
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper5.Icon, { source: icon, size: 64, color: accent }),
314
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper5.Text, { variant: "headlineLarge", style: [styles5.title, { color: accent }], children: title }),
315
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper5.Text, { variant: "bodyLarge", style: [styles5.body, { color: fg }], children: message }),
316
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native7.View, { style: overlayActionStyles.actions, children: [
317
- showCancel && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_feedback_press.Button, { mode: "outlined", onPress: onCancel, style: overlayActionStyles.actionButton, children: cancelLabel }),
318
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_feedback_press.Button, { mode: "contained", onPress: onConfirm, style: showCancel ? overlayActionStyles.actionButton : overlayActionStyles.actionButtonSingle, buttonColor: accent, textColor: onAccent, children: confirmLabel })
392
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper6.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(OverlayBackdrop, { style: styles7.overlay, onLayout, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(OverlayCard, { style: [styles7.card, (0, import_core2.toRotationStyle)(rotation)], children: [
393
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DialogHeader, { icon, title, color: accent, compact }),
394
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper6.Text, { variant: "bodyLarge", style: [styles7.body, { color: fg }], children: message }),
395
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native10.View, { style: overlayActionStyles.actions, children: [
396
+ showCancel && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press.Button, { mode: "outlined", onPress: onCancel, style: overlayActionStyles.actionButton, children: cancelLabel }),
397
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press.Button, { mode: "contained", onPress: onConfirm, style: showCancel ? overlayActionStyles.actionButton : overlayActionStyles.actionButtonSingle, buttonColor: accent, textColor: onAccent, children: confirmLabel })
319
398
  ] })
320
399
  ] }) }) });
321
400
  }
322
- var styles5 = import_react_native7.StyleSheet.create({
401
+ var styles7 = import_react_native10.StyleSheet.create({
323
402
  body: {
324
403
  textAlign: "center"
325
404
  },
@@ -337,35 +416,28 @@ var styles5 = import_react_native7.StyleSheet.create({
337
416
  // screen narrower than maxWidth's own 360.
338
417
  overlay: {
339
418
  paddingHorizontal: 24
340
- },
341
- // textAlign is load-bearing, not decorative, once `title` wraps to more than one line - centered
342
- // within card's own alignItems: 'center' only centers the Text's block as a whole; each wrapped
343
- // line inside that block still defaults to left-aligned without this.
344
- title: {
345
- fontWeight: "bold",
346
- textAlign: "center"
347
419
  }
348
420
  });
349
421
 
350
422
  // src/RotatedFrame.tsx
351
- var import_core2 = require("@tastic/core");
352
- var import_react2 = require("react");
353
- var import_react_native8 = require("react-native");
354
- var import_react_native_paper6 = require("react-native-paper");
423
+ var import_core3 = require("@tastic/core");
424
+ var import_react3 = require("react");
425
+ var import_react_native11 = require("react-native");
426
+ var import_react_native_paper7 = require("react-native-paper");
355
427
  var import_react_native_safe_area_context = require("react-native-safe-area-context");
356
- var import_jsx_runtime7 = require("react/jsx-runtime");
428
+ var import_jsx_runtime9 = require("react/jsx-runtime");
357
429
  function RotatedFrame({ children, rotation: rotationOverride, rotatedSafeArea = false, portalHost = false, passThrough = false, maxContentWidth }) {
358
- const ambientRotation = (0, import_core2.useRotation)();
430
+ const ambientRotation = (0, import_core3.useRotation)();
359
431
  const rotation = rotationOverride ?? ambientRotation;
360
- const window = (0, import_react_native8.useWindowDimensions)();
361
- const { width, height } = (0, import_core2.rotateDimensions)(window.width, window.height, rotation);
432
+ const window = (0, import_react_native11.useWindowDimensions)();
433
+ const { width, height } = (0, import_core3.rotateDimensions)(window.width, window.height, rotation);
362
434
  const rawInsets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
363
- const insets = (0, import_react2.useMemo)(() => rotatedSafeArea ? (0, import_core2.rotateInsets)(rawInsets, rotation) : rawInsets, [rotatedSafeArea, rawInsets, rotation]);
435
+ const insets = (0, import_react3.useMemo)(() => rotatedSafeArea ? (0, import_core3.rotateInsets)(rawInsets, rotation) : rawInsets, [rotatedSafeArea, rawInsets, rotation]);
364
436
  const cappedWidth = maxContentWidth !== void 0 && width > height ? maxContentWidth : void 0;
365
- const column = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native8.View, { style: [styles6.column, cappedWidth !== void 0 && { maxWidth: cappedWidth }], pointerEvents: passThrough ? "box-none" : void 0, children });
366
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_core2.FakeLandscapeView, { rotation, passThrough, style: styles6.root, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_safe_area_context.SafeAreaInsetsContext.Provider, { value: insets, children: portalHost ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Portal.Host, { children: column }) : column }) });
437
+ const column = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native11.View, { style: [styles8.column, cappedWidth !== void 0 && { maxWidth: cappedWidth }], pointerEvents: passThrough ? "box-none" : void 0, children });
438
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_core3.FakeLandscapeView, { rotation, passThrough, style: styles8.root, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_safe_area_context.SafeAreaInsetsContext.Provider, { value: insets, children: portalHost ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper7.Portal.Host, { children: column }) : column }) });
367
439
  }
368
- var styles6 = import_react_native8.StyleSheet.create({
440
+ var styles8 = import_react_native11.StyleSheet.create({
369
441
  // A centred column: full width up to `maxWidth`, so a capped frame sits in the middle of a wider window.
370
442
  column: {
371
443
  alignSelf: "center",
@@ -378,18 +450,18 @@ var styles6 = import_react_native8.StyleSheet.create({
378
450
  });
379
451
 
380
452
  // src/useUpdateConfirmBridge.ts
381
- var import_react3 = require("react");
453
+ var import_react4 = require("react");
382
454
  function useUpdateConfirmBridge() {
383
- const [manifest, setManifest] = (0, import_react3.useState)(null);
384
- const resolveRef = (0, import_react3.useRef)(null);
385
- const onConfirm = (0, import_react3.useCallback)(
455
+ const [manifest, setManifest] = (0, import_react4.useState)(null);
456
+ const resolveRef = (0, import_react4.useRef)(null);
457
+ const onConfirm = (0, import_react4.useCallback)(
386
458
  (next) => new Promise((resolve) => {
387
459
  resolveRef.current = resolve;
388
460
  setManifest(next);
389
461
  }),
390
462
  []
391
463
  );
392
- const respond = (0, import_react3.useCallback)((confirmed) => {
464
+ const respond = (0, import_react4.useCallback)((confirmed) => {
393
465
  setManifest(null);
394
466
  resolveRef.current?.(confirmed);
395
467
  resolveRef.current = null;
@@ -398,20 +470,27 @@ function useUpdateConfirmBridge() {
398
470
  }
399
471
 
400
472
  // src/BaseSettingsDialog.tsx
401
- var import_jsx_runtime8 = require("react/jsx-runtime");
473
+ var import_jsx_runtime10 = require("react/jsx-runtime");
402
474
  function SettingIcon({ source, color, containerColor }) {
403
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native9.View, { style: [styles7.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Icon, { source, size: 18, color }) });
475
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: [styles9.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Icon, { source, size: 18, color }) });
404
476
  }
405
477
  function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, onShowHowToPlay, children }) {
406
- const { colors } = (0, import_auto_paper6.useAutoPaperTheme)();
407
- const ambientRotation = (0, import_core3.useRotation)();
478
+ const { colors } = (0, import_auto_paper8.useAutoPaperTheme)();
479
+ const ambientRotation = (0, import_core4.useRotation)();
408
480
  const rotation = rotationOverride ?? ambientRotation;
409
- const isTouchPrimary = (0, import_core3.useIsTouchPrimaryDevice)();
481
+ const window = (0, import_react_native12.useWindowDimensions)();
482
+ const turned = rotation === 90 || rotation === -90;
483
+ const compactHeader = isCompactDialogHeight(turned ? window.width : window.height);
484
+ const insets = (0, import_core4.rotateInsets)((0, import_react_native_safe_area_context2.useSafeAreaInsets)(), rotation);
485
+ const sheetWidth = Math.min(overlayStyles.card.maxWidth, (turned ? window.height : window.width) - 2 * Math.max(insets.left, insets.right, SHEET_GUTTER));
486
+ const isTouchPrimary = (0, import_core4.useIsTouchPrimaryDevice)();
410
487
  const { settings: hapticSettings, set: setHapticSettings } = (0, import_feedback_press2.useHapticSettings)();
411
488
  const { settings: soundSettings, set: setSoundSettings } = (0, import_feedback_press2.useSoundSettings)();
412
- const sound = (0, import_react4.useContext)(import_feedback_press2.SoundContext);
489
+ const sound = (0, import_react5.useContext)(import_feedback_press2.SoundContext);
413
490
  const { forceShort: forceHapticFeedback } = (0, import_feedback_press2.useVibration)();
414
- const [infoMessage, setInfoMessage] = (0, import_react4.useState)(null);
491
+ const { settings: themeSettings, set: setThemeSettings } = (0, import_auto_paper8.useThemeSettings)();
492
+ const expoBlur = (0, import_auto_paper8.useBlurModule)();
493
+ const [infoMessage, setInfoMessage] = (0, import_react5.useState)(null);
415
494
  const { manifest: updateManifest, onConfirm: onUpdateConfirm, respond: respondToUpdateConfirm } = useUpdateConfirmBridge();
416
495
  const { check, checking, updateReady } = (0, import_updater.useUpdater)({
417
496
  autoCheck: false,
@@ -421,112 +500,124 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
421
500
  onInfo: (title, message) => setInfoMessage({ title, message })
422
501
  });
423
502
  const updateManifestDate = updateManifest ? new Date(updateManifest.createdAt) : null;
424
- const [opened, setOpened] = (0, import_react4.useState)(visible);
503
+ const [opened, setOpened] = (0, import_react5.useState)(visible);
425
504
  if (visible && !opened) setOpened(true);
426
505
  const showLockOrientation = isTouchPrimary && lockOrientation !== void 0 && onLockOrientationChange !== void 0;
427
- const showEdgeGuard = import_react_native9.Platform.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
428
- const showHaptics = import_react_native9.Platform.OS !== "web" && !hideHaptics;
506
+ const showEdgeGuard = import_react_native12.Platform.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
507
+ const showHaptics = import_react_native12.Platform.OS !== "web" && !hideHaptics;
429
508
  const showSound = !hideSound;
430
509
  const showAppearance = !hideAppearance;
431
- const showUpdateCheck = import_react_native9.Platform.OS !== "web" && !hideUpdateCheck;
510
+ const showBlur = showAppearance && import_react_native12.Platform.OS !== "android" && expoBlur !== void 0 && expoBlur !== null;
511
+ const showUpdateCheck = import_react_native12.Platform.OS !== "web" && !hideUpdateCheck;
432
512
  if (!opened) return null;
433
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(RotatedFrame, { rotation, rotatedSafeArea: true, portalHost: true, passThrough: true, children: [
434
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_auto_paper6.Dialog, { visible, onDismiss, style: styles7.dialog, children: [
435
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper6.Dialog.Title, { children: "Settings" }),
436
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper6.Dialog.ScrollArea, { style: styles7.scrollArea, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.ScrollView, { contentContainerStyle: styles7.content, showsVerticalScrollIndicator: false, children: [
437
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleGroup, children: [
438
- showLockOrientation && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles7.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleContent, children: [
439
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.tertiary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.tertiaryContainer : colors.surfaceVariant }),
440
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.flexShrink, children: [
441
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
442
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
443
- ] })
444
- ] }) }),
445
- showEdgeGuard && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles7.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleContent, children: [
446
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.tertiary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.tertiaryContainer : colors.surfaceVariant }),
447
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.flexShrink, children: [
448
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
449
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
450
- ] })
451
- ] }) }),
452
- (showSound || showHaptics) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleRow, children: [
453
- showSound && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
454
- import_feedback_press2.TouchableRipple,
455
- {
456
- soundDisabled: true,
457
- onPress: () => {
458
- const enabled = !soundSettings.enabled;
459
- setSoundSettings({ enabled });
460
- if (enabled) sound.selection?.();
461
- },
462
- style: styles7.toggleButtonInRow,
463
- accessibilityLabel: `Sound ${soundSettings.enabled ? "on" : "off"}`,
464
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleContent, children: [
465
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
466
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
467
- ] })
468
- }
469
- ),
470
- showHaptics && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
513
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(RotatedFrame, { rotation, rotatedSafeArea: true, portalHost: true, passThrough: true, children: [
514
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_auto_paper8.Dialog, { visible, onDismiss, style: [styles9.dialog, { width: sheetWidth }], children: [
515
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_auto_paper8.Dialog.ScrollArea, { style: styles9.scrollArea, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_auto_paper8.FadingScrollView, { contentContainerStyle: styles9.scrollContent, showsVerticalScrollIndicator: false, children: [
516
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DialogHeader, { icon: "cog", title: "Settings", compact: compactHeader }),
517
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.content, children: [
518
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.toggleGroup, children: [
519
+ showLockOrientation && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles9.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.toggleContent, children: [
520
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.tertiary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.tertiaryContainer : colors.surfaceVariant }),
521
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.flexShrink, children: [
522
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
523
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pin current layout" })
524
+ ] })
525
+ ] }) }),
526
+ showEdgeGuard && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles9.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.toggleContent, children: [
527
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.tertiary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.tertiaryContainer : colors.surfaceVariant }),
528
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.flexShrink, children: [
529
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
530
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Require second swipe" })
531
+ ] })
532
+ ] }) }),
533
+ (showSound || showHaptics) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.toggleRow, children: [
534
+ showSound && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
535
+ import_feedback_press2.TouchableRipple,
536
+ {
537
+ soundDisabled: true,
538
+ onPress: () => {
539
+ const enabled = !soundSettings.enabled;
540
+ setSoundSettings({ enabled });
541
+ if (enabled) sound.selection?.();
542
+ },
543
+ style: styles9.toggleButtonInRow,
544
+ accessibilityLabel: `Sound ${soundSettings.enabled ? "on" : "off"}`,
545
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.toggleContent, children: [
546
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
547
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
548
+ ] })
549
+ }
550
+ ),
551
+ showHaptics && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
552
+ import_feedback_press2.TouchableRipple,
553
+ {
554
+ hapticDisabled: true,
555
+ onPress: () => {
556
+ const vibrate = !hapticSettings.vibrate;
557
+ setHapticSettings({ vibrate });
558
+ if (vibrate) forceHapticFeedback();
559
+ },
560
+ style: styles9.toggleButtonInRow,
561
+ accessibilityLabel: `Haptics ${hapticSettings.vibrate ? "on" : "off"}`,
562
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.toggleContent, children: [
563
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
564
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
565
+ ] })
566
+ }
567
+ )
568
+ ] }),
569
+ onShowHowToPlay && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
471
570
  import_feedback_press2.TouchableRipple,
472
571
  {
473
- hapticDisabled: true,
474
572
  onPress: () => {
475
- const vibrate = !hapticSettings.vibrate;
476
- setHapticSettings({ vibrate });
477
- if (vibrate) forceHapticFeedback();
573
+ onDismiss();
574
+ onShowHowToPlay();
478
575
  },
479
- style: styles7.toggleButtonInRow,
480
- accessibilityLabel: `Haptics ${hapticSettings.vibrate ? "on" : "off"}`,
481
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleContent, children: [
482
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
483
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
576
+ style: styles9.toggleButton,
577
+ accessibilityLabel: "How to Play",
578
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.toggleContent, children: [
579
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SettingIcon, { source: "help-circle-outline", color: colors.secondary, containerColor: colors.secondaryContainer }),
580
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.flexShrink, children: [
581
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "How to Play" }),
582
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Replay quick intro" })
583
+ ] })
484
584
  ] })
485
585
  }
486
586
  )
487
587
  ] }),
488
- onShowHowToPlay && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
489
- import_feedback_press2.TouchableRipple,
490
- {
491
- onPress: () => {
492
- onDismiss();
493
- onShowHowToPlay();
494
- },
495
- style: styles7.toggleButton,
496
- accessibilityLabel: "How to Play",
497
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.toggleContent, children: [
498
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SettingIcon, { source: "help-circle-outline", color: colors.secondary, containerColor: colors.secondaryContainer }),
499
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.flexShrink, children: [
500
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "How to Play" }),
501
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Replay the quick intro" })
502
- ] })
588
+ showAppearance && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.section, children: [
589
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Text, { variant: "labelMedium", style: [styles9.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
590
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_auto_paper8.AutoAppearancePicker, { showLabels: false }),
591
+ showBlur && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => setThemeSettings({ blur: !themeSettings.blur }), style: styles9.toggleButton, accessibilityLabel: `Blur ${themeSettings.blur ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.toggleContent, children: [
592
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SettingIcon, { source: themeSettings.blur ? "blur" : "blur-off", color: themeSettings.blur ? colors.tertiary : colors.onSurfaceVariant, containerColor: themeSettings.blur ? colors.tertiaryContainer : colors.surfaceVariant }),
593
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.flexShrink, children: [
594
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Blur" }),
595
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper8.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Frost behind dialogs" })
503
596
  ] })
504
- }
505
- )
506
- ] }),
507
- showAppearance && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.section, children: [
508
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: "labelMedium", style: [styles7.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
509
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper6.AutoAppearancePicker, { showLabels: false })
510
- ] }),
511
- children,
512
- showUpdateCheck && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: styles7.section, children: [
513
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native_paper7.Text, { variant: "labelSmall", style: [styles7.sectionLabel, { color: colors.onSurfaceVariant }], children: [
514
- "VERSION ",
515
- version,
516
- updateReady ? " \xB7 UPDATE READY" : ""
597
+ ] }) })
517
598
  ] }),
518
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press2.Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
599
+ children,
600
+ showUpdateCheck && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: styles9.section, children: [
601
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native_paper8.Text, { variant: "labelMedium", style: [styles9.sectionLabel, { color: colors.onSurfaceVariant }], children: [
602
+ "VERSION ",
603
+ version,
604
+ updateReady ? " \xB7 UPDATE READY" : ""
605
+ ] }),
606
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press2.Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
607
+ ] })
519
608
  ] })
520
- ] }) })
609
+ ] }) }),
610
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: [overlayActionStyles.actions, styles9.footer], children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press2.Button, { mode: "contained", onPress: onDismiss, style: overlayActionStyles.actionButtonSingle, buttonColor: colors.primary, textColor: colors.onPrimary, children: "Done" }) })
521
611
  ] }),
522
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ConfirmDialog, { visible: !!updateManifest, title: "Update Available", message: updateManifestDate ? `Released ${updateManifestDate.toLocaleDateString()} at ${updateManifestDate.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation: 0, onConfirm: () => respondToUpdateConfirm(true), onCancel: () => respondToUpdateConfirm(false) }),
523
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ConfirmDialog, { visible: !!infoMessage, title: infoMessage?.title ?? "", message: infoMessage?.message ?? "", confirmLabel: "OK", icon: "information-outline", rotation: 0, onConfirm: () => setInfoMessage(null) })
612
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ConfirmDialog, { visible: !!updateManifest, title: "Update Available", message: updateManifestDate ? `Released ${updateManifestDate.toLocaleDateString()} at ${updateManifestDate.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation: 0, onConfirm: () => respondToUpdateConfirm(true), onCancel: () => respondToUpdateConfirm(false) }),
613
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ConfirmDialog, { visible: !!infoMessage, title: infoMessage?.title ?? "", message: infoMessage?.message ?? "", confirmLabel: "OK", icon: "information-outline", rotation: 0, onConfirm: () => setInfoMessage(null) })
524
614
  ] }) });
525
615
  }
526
- var styles7 = import_react_native9.StyleSheet.create({
616
+ var SHEET_GUTTER = 24;
617
+ var styles9 = import_react_native12.StyleSheet.create({
618
+ // The rows' sections, 24pt apart.
527
619
  content: {
528
- gap: 24,
529
- paddingVertical: 20
620
+ gap: 24
530
621
  },
531
622
  // Caps the card so it never grows past the screen - without this the dialog just keeps
532
623
  // growing to fit its content and the overflow gets clipped by the screen edge, which is
@@ -538,6 +629,11 @@ var styles7 = import_react_native9.StyleSheet.create({
538
629
  flexShrink: {
539
630
  flexShrink: 1
540
631
  },
632
+ // The overlay cards' 24pt padding, as the sheet's own insets (its scroll area pads 24 sideways).
633
+ footer: {
634
+ paddingBottom: 24,
635
+ paddingHorizontal: 24
636
+ },
541
637
  iconBadge: {
542
638
  alignItems: "center",
543
639
  borderRadius: 10,
@@ -545,9 +641,20 @@ var styles7 = import_react_native9.StyleSheet.create({
545
641
  justifyContent: "center",
546
642
  width: 36
547
643
  },
644
+ // No hairline dividers above and below the scroll area (Paper's MD2-era borders): none of the
645
+ // fleet's other dialogs draws one, so the sheet matches them (Jay, 2026-10-15).
548
646
  scrollArea: {
647
+ borderBottomWidth: 0,
648
+ borderTopWidth: 0,
549
649
  marginBottom: 0
550
650
  },
651
+ // The header and the rows scroll together: the card's 24pt inset above the header, the overlay
652
+ // cards' 16pt gap between it and the rows, and 16pt below the last row before the Done row.
653
+ scrollContent: {
654
+ gap: 16,
655
+ paddingBottom: 16,
656
+ paddingTop: 24
657
+ },
551
658
  section: {
552
659
  gap: 12
553
660
  },
@@ -591,31 +698,31 @@ var styles7 = import_react_native9.StyleSheet.create({
591
698
  });
592
699
 
593
700
  // src/BaseStatsScreen.tsx
594
- var import_auto_paper7 = require("@rific/auto-paper");
701
+ var import_auto_paper9 = require("@rific/auto-paper");
595
702
  var import_feedback_press3 = require("@rific/feedback-press");
596
- var import_core4 = require("@tastic/core");
597
- var import_react5 = require("react");
598
- var import_react_native10 = require("react-native");
599
- var import_react_native_paper8 = require("react-native-paper");
600
- var import_jsx_runtime9 = require("react/jsx-runtime");
703
+ var import_core5 = require("@tastic/core");
704
+ var import_react6 = require("react");
705
+ var import_react_native13 = require("react-native");
706
+ var import_react_native_paper9 = require("react-native-paper");
707
+ var import_jsx_runtime11 = require("react/jsx-runtime");
601
708
  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 }) {
602
- const { dark, colors } = (0, import_auto_paper7.useAutoPaperTheme)();
603
- const ambientRotation = (0, import_core4.useRotation)();
709
+ const { dark, colors } = (0, import_auto_paper9.useAutoPaperTheme)();
710
+ const ambientRotation = (0, import_core5.useRotation)();
604
711
  const rotation = rotationOverride ?? ambientRotation;
605
- const [confirmResetVisible, setConfirmResetVisible] = (0, import_react5.useState)(false);
712
+ const [confirmResetVisible, setConfirmResetVisible] = (0, import_react6.useState)(false);
606
713
  const showReset = !!onReset;
607
714
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
608
715
  const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
609
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: [styles8.container, { backgroundColor: bg }], children: [
610
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: [styles8.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
611
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
612
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: titleVariant, style: [styles8.title, { color: fg }], children: title })
716
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native13.View, { style: [styles10.container, { backgroundColor: bg }], children: [
717
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native13.View, { style: [styles10.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
718
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
719
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native_paper9.Text, { variant: titleVariant, style: [styles10.title, { color: fg }], children: title })
613
720
  ] }),
614
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.ScrollView, { style: styles8.scrollView, contentContainerStyle: [styles8.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
721
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native13.ScrollView, { style: styles10.scrollView, contentContainerStyle: [styles10.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
615
722
  children,
616
- showReset && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press3.Button, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles8.resetButton, children: resetLabel })
723
+ showReset && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_feedback_press3.Button, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles10.resetButton, children: resetLabel })
617
724
  ] }),
618
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
725
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
619
726
  ConfirmDialog,
620
727
  {
621
728
  visible: confirmResetVisible,
@@ -635,7 +742,7 @@ function BaseStatsScreen({ rotation: rotationOverride, title = "Achievements", t
635
742
  )
636
743
  ] });
637
744
  }
638
- var styles8 = import_react_native10.StyleSheet.create({
745
+ var styles10 = import_react_native13.StyleSheet.create({
639
746
  container: {
640
747
  flex: 1
641
748
  },
@@ -666,19 +773,19 @@ var styles8 = import_react_native10.StyleSheet.create({
666
773
  });
667
774
 
668
775
  // src/ContentGutter.tsx
669
- var import_core5 = require("@tastic/core");
670
- var import_react_native11 = require("react-native");
671
- var import_jsx_runtime10 = require("react/jsx-runtime");
776
+ var import_core6 = require("@tastic/core");
777
+ var import_react_native14 = require("react-native");
778
+ var import_jsx_runtime12 = require("react/jsx-runtime");
672
779
  function ContentGutter({ maxContentWidth, leftGutter, rightGutter, style, children }) {
673
- const { width: windowWidth } = (0, import_core5.useRotatedWindowDimensions)();
674
- const { contentWidth, gutterWidth } = (0, import_core5.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
675
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: [styles9.row, style], children: [
676
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native11.View, { style: [styles9.gutter, { width: gutterWidth }], children: leftGutter }),
677
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native11.View, { style: { width: contentWidth }, children }),
678
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native11.View, { style: [styles9.gutter, { width: gutterWidth }], children: rightGutter })
780
+ const { width: windowWidth } = (0, import_core6.useRotatedWindowDimensions)();
781
+ const { contentWidth, gutterWidth } = (0, import_core6.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
782
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native14.View, { style: [styles11.row, style], children: [
783
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native14.View, { style: [styles11.gutter, { width: gutterWidth }], children: leftGutter }),
784
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native14.View, { style: { width: contentWidth }, children }),
785
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native14.View, { style: [styles11.gutter, { width: gutterWidth }], children: rightGutter })
679
786
  ] });
680
787
  }
681
- var styles9 = import_react_native11.StyleSheet.create({
788
+ var styles11 = import_react_native14.StyleSheet.create({
682
789
  // overflow:'hidden' keeps an oversized leftGutter/rightGutter from bleeding into the content
683
790
  // column instead of clipping cleanly at its own real width.
684
791
  gutter: {
@@ -691,15 +798,15 @@ var styles9 = import_react_native11.StyleSheet.create({
691
798
 
692
799
  // src/CornerActionButtons.tsx
693
800
  var import_feedback_press4 = require("@rific/feedback-press");
694
- var import_react_native12 = require("react-native");
695
- var import_jsx_runtime11 = require("react/jsx-runtime");
801
+ var import_react_native15 = require("react-native");
802
+ var import_jsx_runtime13 = require("react/jsx-runtime");
696
803
  function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon = "arrow-left", backLabel = "Back", hitSlop }) {
697
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
698
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_feedback_press4.IconButton, { icon: backIcon, iconColor: fg, size: 24, style: [styles10.back, { top: 8 + insets.top, left: 8 + insets.left }], hitSlop, onPress: onBack, accessibilityLabel: backLabel }),
699
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_feedback_press4.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles10.topRight, { top: 8 + insets.top, right: 8 + insets.right }], hitSlop, onPress: onSettings, accessibilityLabel: "Settings" })
804
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
805
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press4.IconButton, { icon: backIcon, iconColor: fg, size: 24, style: [styles12.back, { top: 8 + insets.top, left: 8 + insets.left }], hitSlop, onPress: onBack, accessibilityLabel: backLabel }),
806
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_feedback_press4.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles12.topRight, { top: 8 + insets.top, right: 8 + insets.right }], hitSlop, onPress: onSettings, accessibilityLabel: "Settings" })
700
807
  ] });
701
808
  }
702
- var styles10 = import_react_native12.StyleSheet.create({
809
+ var styles12 = import_react_native15.StyleSheet.create({
703
810
  back: {
704
811
  position: "absolute"
705
812
  },
@@ -709,10 +816,10 @@ var styles10 = import_react_native12.StyleSheet.create({
709
816
  });
710
817
 
711
818
  // src/StaggeredWord.tsx
712
- var import_react6 = require("react");
713
- var import_react_native13 = require("react-native");
819
+ var import_react7 = require("react");
820
+ var import_react_native16 = require("react-native");
714
821
  var import_react_native_reanimated = __toESM(require("react-native-reanimated"));
715
- var import_jsx_runtime12 = require("react/jsx-runtime");
822
+ var import_jsx_runtime14 = require("react/jsx-runtime");
716
823
  var STAGGER_MS = 45;
717
824
  var LETTER_DURATION_MS = 320;
718
825
  function getStaggeredWordDuration(letterCount) {
@@ -720,7 +827,7 @@ function getStaggeredWordDuration(letterCount) {
720
827
  }
721
828
  function AnimatedLetter({ char, index, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }) {
722
829
  const progress = (0, import_react_native_reanimated.useSharedValue)(reducedMotion ? 1 : 0);
723
- (0, import_react6.useEffect)(() => {
830
+ (0, import_react7.useEffect)(() => {
724
831
  if (reducedMotion) return;
725
832
  progress.value = (0, import_react_native_reanimated.withDelay)(index * STAGGER_MS, (0, import_react_native_reanimated.withTiming)(1, { duration: LETTER_DURATION_MS, easing: import_react_native_reanimated.Easing.out(import_react_native_reanimated.Easing.back(1.5)) }));
726
833
  }, [reducedMotion]);
@@ -728,12 +835,12 @@ function AnimatedLetter({ char, index, color, reducedMotion, fontFamily, fontSiz
728
835
  opacity: progress.value,
729
836
  transform: [{ translateY: (1 - progress.value) * 12 }, { scale: 0.85 + progress.value * 0.15 }]
730
837
  }));
731
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_reanimated.default.Text, { allowFontScaling: false, style: [{ color, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, style], children: char });
838
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_reanimated.default.Text, { allowFontScaling: false, style: [{ color, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, style], children: char });
732
839
  }
733
840
  function StaggeredWord({ word, color, fontFamily, fontSize, lineHeight, reducedMotion, startIndex = 0, fontWeight = "bold", letterSpacing, style, onLayout }) {
734
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { style: [styles11.row, style], onLayout, children: word.split("").map((char, i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AnimatedLetter, { char, index: startIndex + i, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, i)) });
841
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native16.View, { style: [styles13.row, style], onLayout, children: word.split("").map((char, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AnimatedLetter, { char, index: startIndex + i, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, i)) });
735
842
  }
736
- var styles11 = import_react_native13.StyleSheet.create({
843
+ var styles13 = import_react_native16.StyleSheet.create({
737
844
  row: {
738
845
  flexDirection: "row"
739
846
  }
@@ -741,12 +848,12 @@ var styles11 = import_react_native13.StyleSheet.create({
741
848
 
742
849
  // src/UpdateDialog.tsx
743
850
  var import_updater2 = require("@rific/updater");
744
- var import_jsx_runtime13 = require("react/jsx-runtime");
851
+ var import_jsx_runtime15 = require("react/jsx-runtime");
745
852
  function UpdateDialog({ rotation = 0, autoCheck = true, autoPrompt = true, onError }) {
746
853
  const { manifest, onConfirm, respond } = useUpdateConfirmBridge();
747
854
  (0, import_updater2.useUpdater)({ autoCheck, autoPrompt, onConfirm, onError });
748
855
  const date = manifest ? new Date(manifest.createdAt) : null;
749
- return /* @__PURE__ */ (0, import_jsx_runtime13.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) });
856
+ return /* @__PURE__ */ (0, import_jsx_runtime15.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) });
750
857
  }
751
858
  // Annotate the CommonJS export names for ESM import in node:
752
859
  0 && (module.exports = {
@@ -755,18 +862,24 @@ function UpdateDialog({ rotation = 0, autoCheck = true, autoPrompt = true, onErr
755
862
  ActivityStatSection,
756
863
  BaseSettingsDialog,
757
864
  BaseStatsScreen,
865
+ COMPACT_DIALOG_HEIGHT,
758
866
  ConfirmDialog,
759
867
  ContentGutter,
760
868
  CornerActionButtons,
869
+ DialogHeader,
761
870
  LOCKED_BADGE_COLOR,
762
871
  MONO_FONT,
872
+ OverlayBackdrop,
873
+ OverlayCard,
763
874
  RotatedFrame,
764
875
  StaggeredWord,
765
876
  StatRow,
766
877
  StatSection,
767
878
  UpdateDialog,
768
- getOverlayCardColors,
769
879
  getStaggeredWordDuration,
880
+ isCompactDialogHeight,
770
881
  overlayActionStyles,
771
- overlayStyles
882
+ overlayStyles,
883
+ useCompactDialog,
884
+ useOverlayCardColors
772
885
  });