@tastic/hud 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -38,22 +38,22 @@ __export(TriggerGauge_exports, {
38
38
  function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, dashWidth, startDeg = DEFAULT_START_DEG, endDeg = startDeg }) {
39
39
  const totalDurationMs = MOUNT_DURATION_MS + Math.max(0, segments - 1) * MOUNT_STAGGER_MS;
40
40
  const mountProgress = (0, import_react_native_reanimated.useSharedValue)(0);
41
- (0, import_react6.useLayoutEffect)(() => {
41
+ (0, import_react7.useLayoutEffect)(() => {
42
42
  mountProgress.value = (0, import_react_native_reanimated.withTiming)(1, { duration: totalDurationMs, easing: import_react_native_reanimated.Easing.out(import_react_native_reanimated.Easing.cubic) });
43
43
  }, []);
44
44
  const rawSweepDeg = ((endDeg - startDeg) % 360 + 360) % 360;
45
45
  const totalSweepDeg = rawSweepDeg === 0 ? 360 : rawSweepDeg;
46
46
  const step = segments > 0 ? totalSweepDeg / segments : 0;
47
- const centerDeg = (0, import_react6.useCallback)((i) => startDeg + (i + 0.5) * step, [startDeg, step]);
47
+ const centerDeg = (0, import_react7.useCallback)((i) => startDeg + (i + 0.5) * step, [startDeg, step]);
48
48
  const singleSelect = litIndices.length === 1;
49
49
  const angleDeg = (0, import_react_native_reanimated.useSharedValue)(centerDeg(litIndices[0] ?? 0));
50
50
  const litIndicesKey = [...litIndices].sort((a, b) => a - b).join(",");
51
- const [transitionFrom, setTransitionFrom] = (0, import_react6.useState)(litIndices);
51
+ const [transitionFrom, setTransitionFrom] = (0, import_react7.useState)(litIndices);
52
52
  const transitionFromKey = [...transitionFrom].sort((a, b) => a - b).join(",");
53
- const lastKeyRef = (0, import_react6.useRef)(litIndicesKey);
54
- const lastIndicesRef = (0, import_react6.useRef)(litIndices);
53
+ const lastKeyRef = (0, import_react7.useRef)(litIndicesKey);
54
+ const lastIndicesRef = (0, import_react7.useRef)(litIndices);
55
55
  const selectionProgress = (0, import_react_native_reanimated.useSharedValue)(1);
56
- (0, import_react6.useLayoutEffect)(() => {
56
+ (0, import_react7.useLayoutEffect)(() => {
57
57
  if (litIndicesKey !== lastKeyRef.current) {
58
58
  if (singleSelect && lastIndicesRef.current.length === 1) {
59
59
  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) });
@@ -82,7 +82,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
82
82
  if (mountProgress.value < 1) {
83
83
  const startFrac = i * MOUNT_STAGGER_MS / totalDurationMs;
84
84
  const endFrac = startFrac + MOUNT_DURATION_MS / totalDurationMs;
85
- local = (0, import_core5.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
85
+ local = (0, import_core6.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
86
86
  }
87
87
  const sweep = dashAngleDeg * local;
88
88
  if (sweep <= 0) continue;
@@ -106,7 +106,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
106
106
  if (!isLitNow) continue;
107
107
  const startFrac = i * MOUNT_STAGGER_MS / totalDurationMs;
108
108
  const endFrac = startFrac + MOUNT_DURATION_MS / totalDurationMs;
109
- local = (0, import_core5.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
109
+ local = (0, import_core6.clamp)((mountProgress.value - startFrac) / (endFrac - startFrac), 0, 1);
110
110
  } else if (isLitNow === wasLit) {
111
111
  local = 1;
112
112
  } else if (isLitNow) {
@@ -130,21 +130,21 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
130
130
  height: canvasSize,
131
131
  width: canvasSize
132
132
  };
133
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.View, { style: [styles10.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
134
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_skia.Path, { path: unlitPath, style: "stroke", strokeWidth: UNLIT_THICKNESS, strokeCap: "round", color: mutedColor, opacity: 0.35 }),
135
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_skia.Path, { path: litPath, style: "stroke", strokeWidth: LIT_THICKNESS, strokeCap: "round", color: accentColor, opacity: 1 })
133
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native14.View, { style: [styles12.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
134
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_skia.Path, { path: unlitPath, style: "stroke", strokeWidth: UNLIT_THICKNESS, strokeCap: "round", color: mutedColor, opacity: 0.35 }),
135
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_skia.Path, { path: litPath, style: "stroke", strokeWidth: LIT_THICKNESS, strokeCap: "round", color: accentColor, opacity: 1 })
136
136
  ] }) });
137
137
  }
138
- var import_react_native_skia, import_core5, import_react6, import_react_native12, import_react_native_reanimated, import_jsx_runtime12, 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, styles10;
138
+ var import_react_native_skia, import_core6, import_react7, import_react_native14, import_react_native_reanimated, import_jsx_runtime14, 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, styles12;
139
139
  var init_TriggerGauge = __esm({
140
140
  "src/TriggerGauge.tsx"() {
141
141
  "use strict";
142
142
  import_react_native_skia = require("@shopify/react-native-skia");
143
- import_core5 = require("@tastic/core");
144
- import_react6 = require("react");
145
- import_react_native12 = require("react-native");
143
+ import_core6 = require("@tastic/core");
144
+ import_react7 = require("react");
145
+ import_react_native14 = require("react-native");
146
146
  import_react_native_reanimated = require("react-native-reanimated");
147
- import_jsx_runtime12 = require("react/jsx-runtime");
147
+ import_jsx_runtime14 = require("react/jsx-runtime");
148
148
  UNLIT_THICKNESS = 3;
149
149
  LIT_THICKNESS = 7;
150
150
  DASH_GAP_PX = 9;
@@ -154,7 +154,7 @@ var init_TriggerGauge = __esm({
154
154
  MOUNT_DURATION_MS = 450;
155
155
  MOUNT_STAGGER_MS = 40;
156
156
  SELECTION_TRANSITION_DURATION_MS = 260;
157
- styles10 = import_react_native12.StyleSheet.create({
157
+ styles12 = import_react_native14.StyleSheet.create({
158
158
  container: {
159
159
  alignItems: "center",
160
160
  justifyContent: "center",
@@ -169,6 +169,7 @@ var index_exports = {};
169
169
  __export(index_exports, {
170
170
  AchievementCatalogSection: () => AchievementCatalogSection,
171
171
  AchievementRow: () => AchievementRow,
172
+ AchievementUnlockList: () => AchievementUnlockList,
172
173
  ActivityStatSection: () => ActivityStatSection,
173
174
  BaseSettingsDialog: () => BaseSettingsDialog,
174
175
  BaseStatsScreen: () => BaseStatsScreen,
@@ -186,6 +187,7 @@ __export(index_exports, {
186
187
  PopoverBody: () => PopoverBody,
187
188
  PressAwayOverlay: () => PressAwayOverlay,
188
189
  ReadyButton: () => ReadyButton,
190
+ RotatedFrame: () => RotatedFrame,
189
191
  SectionedDropdown: () => SectionedDropdown,
190
192
  SharedActionBand: () => SharedActionBand,
191
193
  SkiaGate: () => SkiaGate,
@@ -201,6 +203,7 @@ __export(index_exports, {
201
203
  getOverlayCardColors: () => getOverlayCardColors,
202
204
  getStaggeredWordDuration: () => getStaggeredWordDuration,
203
205
  loadSkiaWeb: () => loadSkiaWeb,
206
+ overlayActionStyles: () => overlayActionStyles,
204
207
  overlayStyles: () => overlayStyles,
205
208
  useAutoAlign: () => useAutoAlign,
206
209
  usePopoverHost: () => usePopoverHost,
@@ -304,21 +307,65 @@ var styles2 = import_react_native3.StyleSheet.create({
304
307
  }
305
308
  });
306
309
 
307
- // src/StatRow.tsx
310
+ // src/AchievementUnlockList.tsx
308
311
  var import_auto_paper3 = require("@rific/auto-paper");
312
+ var import_profile = require("@tastic/profile");
309
313
  var import_react_native4 = require("react-native");
310
314
  var import_react_native_paper3 = require("react-native-paper");
311
315
  var import_jsx_runtime3 = require("react/jsx-runtime");
312
- function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }) {
316
+ var AVATAR_SIZE = 18;
317
+ function AchievementUnlockList({ unlocks, seats, owners, tierColors, fg: fgOverride, style }) {
313
318
  const { dark } = (0, import_auto_paper3.useAutoPaperTheme)();
314
319
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
320
+ const rows = seats.flatMap((seat) => (unlocks[seat] ?? []).map((achievement) => ({ seat, achievement })));
321
+ if (rows.length === 0) return null;
322
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native4.View, { style: [styles3.list, style], children: rows.map(({ seat, achievement }) => {
323
+ const owner = owners[seat];
324
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.row, children: [
325
+ owner.profile ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_profile.ProfileChip, { profile: owner.profile, size: AVATAR_SIZE }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native4.View, { style: [styles3.circle, { backgroundColor: owner.color }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Icon, { source: "account", size: 10, color: (0, import_auto_paper3.getContrastColor)(owner.color) }) }),
326
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native4.View, { style: [styles3.circle, { backgroundColor: tierColors[achievement.tier] }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Icon, { source: achievement.icon, size: 12, color: "#000000" }) }),
327
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { style: [styles3.label, { color: fg }], numberOfLines: 1, children: achievement.title })
328
+ ] }, `${seat}-${achievement.id}`);
329
+ }) });
330
+ }
331
+ var styles3 = import_react_native4.StyleSheet.create({
332
+ circle: {
333
+ alignItems: "center",
334
+ borderRadius: AVATAR_SIZE / 2,
335
+ height: AVATAR_SIZE,
336
+ justifyContent: "center",
337
+ width: AVATAR_SIZE
338
+ },
339
+ label: {
340
+ flexShrink: 1,
341
+ fontSize: 12,
342
+ fontWeight: "700"
343
+ },
344
+ list: {
345
+ gap: 6
346
+ },
347
+ row: {
348
+ alignItems: "center",
349
+ flexDirection: "row",
350
+ gap: 6
351
+ }
352
+ });
353
+
354
+ // src/StatRow.tsx
355
+ var import_auto_paper4 = require("@rific/auto-paper");
356
+ var import_react_native5 = require("react-native");
357
+ var import_react_native_paper4 = require("react-native-paper");
358
+ var import_jsx_runtime4 = require("react/jsx-runtime");
359
+ function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }) {
360
+ const { dark } = (0, import_auto_paper4.useAutoPaperTheme)();
361
+ const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
315
362
  const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
316
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles3.row, children: [
317
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
318
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Text, { variant: "bodyMedium", style: [styles3.value, { color: fg }], children: value })
363
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native5.View, { style: styles4.row, children: [
364
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Text, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
365
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Text, { variant: "bodyMedium", style: [styles4.value, { color: fg }], children: value })
319
366
  ] });
320
367
  }
321
- var styles3 = import_react_native4.StyleSheet.create({
368
+ var styles4 = import_react_native5.StyleSheet.create({
322
369
  row: {
323
370
  alignItems: "center",
324
371
  flexDirection: "row",
@@ -330,20 +377,20 @@ var styles3 = import_react_native4.StyleSheet.create({
330
377
  });
331
378
 
332
379
  // src/StatSection.tsx
333
- var import_auto_paper4 = require("@rific/auto-paper");
334
- var import_react_native5 = require("react-native");
335
- var import_react_native_paper4 = require("react-native-paper");
336
- var import_jsx_runtime4 = require("react/jsx-runtime");
380
+ var import_auto_paper5 = require("@rific/auto-paper");
381
+ var import_react_native6 = require("react-native");
382
+ var import_react_native_paper5 = require("react-native-paper");
383
+ var import_jsx_runtime5 = require("react/jsx-runtime");
337
384
  function StatSection({ label, fg: fgOverride, sectionBg: sectionBgOverride, children }) {
338
- const { dark } = (0, import_auto_paper4.useAutoPaperTheme)();
385
+ const { dark } = (0, import_auto_paper5.useAutoPaperTheme)();
339
386
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
340
387
  const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
341
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native5.View, { style: [styles4.section, { backgroundColor: sectionBg }], children: [
342
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Text, { variant: "labelMedium", style: [styles4.label, { color: fg }], children: label }),
388
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native6.View, { style: [styles5.section, { backgroundColor: sectionBg }], children: [
389
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper5.Text, { variant: "labelMedium", style: [styles5.label, { color: fg }], children: label }),
343
390
  children
344
391
  ] });
345
392
  }
346
- var styles4 = import_react_native5.StyleSheet.create({
393
+ var styles5 = import_react_native6.StyleSheet.create({
347
394
  // Bold and full-contrast (fg), not the original fontless/fgMuted treatment — that read at the
348
395
  // same visual weight as StatRow's own row labels below it (both used the identical muted color;
349
396
  // letterSpacing/variant alone didn't separate them enough to read as "this is the group header"
@@ -360,33 +407,33 @@ var styles4 = import_react_native5.StyleSheet.create({
360
407
  });
361
408
 
362
409
  // src/ActivityStatSection.tsx
363
- var import_jsx_runtime5 = require("react/jsx-runtime");
410
+ var import_jsx_runtime6 = require("react/jsx-runtime");
364
411
  function ActivityStatSection({ stats }) {
365
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(StatSection, { label: "ACTIVITY", children: [
366
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatRow, { label: "Days Played", value: String(stats.distinctDaysPlayed) }),
367
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatRow, { label: "Day Streak", value: String(stats.currentDayStreak) }),
368
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatRow, { label: "Best Day Streak", value: String(stats.bestDayStreak) })
412
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(StatSection, { label: "ACTIVITY", children: [
413
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { label: "Days Played", value: String(stats.distinctDaysPlayed) }),
414
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { label: "Day Streak", value: String(stats.currentDayStreak) }),
415
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { label: "Best Day Streak", value: String(stats.bestDayStreak) })
369
416
  ] });
370
417
  }
371
418
 
372
419
  // src/BaseSettingsDialog.tsx
373
- var import_auto_paper6 = require("@rific/auto-paper");
420
+ var import_auto_paper7 = require("@rific/auto-paper");
374
421
  var import_feedback_press2 = require("@rific/feedback-press");
375
422
  var import_updater = require("@rific/updater");
376
- var import_core2 = require("@tastic/core");
377
- var import_react3 = require("react");
378
- var import_react_native8 = require("react-native");
379
- var import_react_native_paper6 = require("react-native-paper");
423
+ var import_core3 = require("@tastic/core");
424
+ var import_react4 = require("react");
425
+ var import_react_native10 = require("react-native");
426
+ var import_react_native_paper8 = require("react-native-paper");
380
427
 
381
428
  // src/ConfirmDialog.tsx
382
- var import_auto_paper5 = require("@rific/auto-paper");
429
+ var import_auto_paper6 = require("@rific/auto-paper");
383
430
  var import_feedback_press = require("@rific/feedback-press");
384
431
  var import_core = require("@tastic/core");
385
- var import_react_native7 = require("react-native");
386
- var import_react_native_paper5 = require("react-native-paper");
432
+ var import_react_native8 = require("react-native");
433
+ var import_react_native_paper6 = require("react-native-paper");
387
434
 
388
435
  // src/overlayCard.ts
389
- var import_react_native6 = require("react-native");
436
+ var import_react_native7 = require("react-native");
390
437
  function getOverlayCardColors(dark) {
391
438
  return {
392
439
  fg: dark ? "#FFFFFF" : "#000000",
@@ -395,7 +442,7 @@ function getOverlayCardColors(dark) {
395
442
  cardBorder: dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)"
396
443
  };
397
444
  }
398
- var overlayStyles = import_react_native6.StyleSheet.create({
445
+ var overlayStyles = import_react_native7.StyleSheet.create({
399
446
  card: {
400
447
  alignItems: "center",
401
448
  borderRadius: 20,
@@ -415,27 +462,7 @@ var overlayStyles = import_react_native6.StyleSheet.create({
415
462
  top: 0
416
463
  }
417
464
  });
418
-
419
- // src/ConfirmDialog.tsx
420
- var import_jsx_runtime6 = require("react/jsx-runtime");
421
- function ConfirmDialog({ visible, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, rotation = 0, icon = "help-circle-outline", destructive = false }) {
422
- const { dark, colors } = (0, import_auto_paper5.useAutoPaperTheme)();
423
- if (!visible) return null;
424
- const { fg, cardBg, cardBorder } = getOverlayCardColors(dark);
425
- const accent = destructive ? colors.danger : colors.primary;
426
- const onAccent = destructive ? colors.onDanger : colors.onPrimary;
427
- const showCancel = cancelLabel !== void 0 && onCancel !== void 0;
428
- 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: [
429
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper5.Icon, { source: icon, size: 64, color: accent }),
430
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper5.Text, { variant: "headlineLarge", style: [styles5.title, { color: accent }], children: title }),
431
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper5.Text, { variant: "bodyLarge", style: [styles5.body, { color: fg }], children: message }),
432
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native7.View, { style: styles5.actions, children: [
433
- showCancel && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_feedback_press.Button, { mode: "outlined", onPress: onCancel, style: styles5.actionButton, children: cancelLabel }),
434
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_feedback_press.Button, { mode: "contained", onPress: onConfirm, style: showCancel ? styles5.actionButton : styles5.actionButtonSingle, buttonColor: accent, textColor: onAccent, children: confirmLabel })
435
- ] })
436
- ] }) }) });
437
- }
438
- var styles5 = import_react_native7.StyleSheet.create({
465
+ var overlayActionStyles = import_react_native7.StyleSheet.create({
439
466
  // minWidth (not flexBasis) is each button's real floor - whenever both fit side by side, flexGrow
440
467
  // still splits any leftover width evenly beyond that floor, same visual result as a plain flex: 1
441
468
  // on a roomy screen, but a button can never be squeezed narrower than 128, only wrap to its own
@@ -454,8 +481,8 @@ var styles5 = import_react_native7.StyleSheet.create({
454
481
  // hardcodes numberOfLines={1} on its label with no override, so a button squeezed under its
455
482
  // label's natural width silently ellipsizes ("Cancel" -> "Can...") instead of wrapping - assumed
456
483
  // fixable by just giving the row more width, until confirmed live that these two-word labels
457
- // (Cancel/Reset/New Game) can still lose the truncation fight even with the extra room card's own
458
- // width fix below provides, on a narrow enough screen.
484
+ // (Cancel/Reset/New Game) can still lose the truncation fight even with the extra room ConfirmDialog's own
485
+ // card width fix provides, on a narrow enough screen.
459
486
  actionButton: {
460
487
  flexGrow: 1,
461
488
  minWidth: 128
@@ -474,7 +501,29 @@ var styles5 = import_react_native7.StyleSheet.create({
474
501
  gap: 12,
475
502
  justifyContent: "center",
476
503
  width: "100%"
477
- },
504
+ }
505
+ });
506
+
507
+ // src/ConfirmDialog.tsx
508
+ var import_jsx_runtime7 = require("react/jsx-runtime");
509
+ function ConfirmDialog({ visible, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, rotation = 0, icon = "help-circle-outline", destructive = false }) {
510
+ const { dark, colors } = (0, import_auto_paper6.useAutoPaperTheme)();
511
+ if (!visible) return null;
512
+ const { fg, cardBg, cardBorder } = getOverlayCardColors(dark);
513
+ const accent = destructive ? colors.danger : colors.primary;
514
+ const onAccent = destructive ? colors.onDanger : colors.onPrimary;
515
+ const showCancel = cancelLabel !== void 0 && onCancel !== void 0;
516
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native8.View, { style: [overlayStyles.overlay, styles6.overlay], children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: [overlayStyles.card, styles6.card, { backgroundColor: cardBg, borderColor: cardBorder }, (0, import_core.toRotationStyle)(rotation)], children: [
517
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Icon, { source: icon, size: 64, color: accent }),
518
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Text, { variant: "headlineLarge", style: [styles6.title, { color: accent }], children: title }),
519
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Text, { variant: "bodyLarge", style: [styles6.body, { color: fg }], children: message }),
520
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: overlayActionStyles.actions, children: [
521
+ showCancel && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press.Button, { mode: "outlined", onPress: onCancel, style: overlayActionStyles.actionButton, children: cancelLabel }),
522
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press.Button, { mode: "contained", onPress: onConfirm, style: showCancel ? overlayActionStyles.actionButton : overlayActionStyles.actionButtonSingle, buttonColor: accent, textColor: onAccent, children: confirmLabel })
523
+ ] })
524
+ ] }) }) });
525
+ }
526
+ var styles6 = import_react_native8.StyleSheet.create({
478
527
  body: {
479
528
  textAlign: "center"
480
529
  },
@@ -502,19 +551,49 @@ var styles5 = import_react_native7.StyleSheet.create({
502
551
  }
503
552
  });
504
553
 
505
- // src/useUpdateConfirmBridge.ts
554
+ // src/RotatedFrame.tsx
555
+ var import_core2 = require("@tastic/core");
506
556
  var import_react2 = require("react");
557
+ var import_react_native9 = require("react-native");
558
+ var import_react_native_paper7 = require("react-native-paper");
559
+ var import_react_native_safe_area_context = require("react-native-safe-area-context");
560
+ var import_jsx_runtime8 = require("react/jsx-runtime");
561
+ function RotatedFrame({ children, rotation: rotationOverride, rotatedSafeArea = false, portalHost = false, passThrough = false, maxContentWidth }) {
562
+ const ambientRotation = (0, import_core2.useRotation)();
563
+ const rotation = rotationOverride ?? ambientRotation;
564
+ const window = (0, import_react_native9.useWindowDimensions)();
565
+ const { width, height } = (0, import_core2.rotateDimensions)(window.width, window.height, rotation);
566
+ const rawInsets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
567
+ const insets = (0, import_react2.useMemo)(() => rotatedSafeArea ? (0, import_core2.rotateInsets)(rawInsets, rotation) : rawInsets, [rotatedSafeArea, rawInsets, rotation]);
568
+ const cappedWidth = maxContentWidth !== void 0 && width > height ? maxContentWidth : void 0;
569
+ const column = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native9.View, { style: [styles7.column, cappedWidth !== void 0 && { maxWidth: cappedWidth }], pointerEvents: passThrough ? "box-none" : void 0, children });
570
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_core2.FakeLandscapeView, { rotation, passThrough, style: styles7.root, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_safe_area_context.SafeAreaInsetsContext.Provider, { value: insets, children: portalHost ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Portal.Host, { children: column }) : column }) });
571
+ }
572
+ var styles7 = import_react_native9.StyleSheet.create({
573
+ // A centred column: full width up to `maxWidth`, so a capped frame sits in the middle of a wider window.
574
+ column: {
575
+ alignSelf: "center",
576
+ flex: 1,
577
+ width: "100%"
578
+ },
579
+ root: {
580
+ flex: 1
581
+ }
582
+ });
583
+
584
+ // src/useUpdateConfirmBridge.ts
585
+ var import_react3 = require("react");
507
586
  function useUpdateConfirmBridge() {
508
- const [manifest, setManifest] = (0, import_react2.useState)(null);
509
- const resolveRef = (0, import_react2.useRef)(null);
510
- const onConfirm = (0, import_react2.useCallback)(
587
+ const [manifest, setManifest] = (0, import_react3.useState)(null);
588
+ const resolveRef = (0, import_react3.useRef)(null);
589
+ const onConfirm = (0, import_react3.useCallback)(
511
590
  (next) => new Promise((resolve) => {
512
591
  resolveRef.current = resolve;
513
592
  setManifest(next);
514
593
  }),
515
594
  []
516
595
  );
517
- const respond = (0, import_react2.useCallback)((confirmed) => {
596
+ const respond = (0, import_react3.useCallback)((confirmed) => {
518
597
  setManifest(null);
519
598
  resolveRef.current?.(confirmed);
520
599
  resolveRef.current = null;
@@ -523,20 +602,20 @@ function useUpdateConfirmBridge() {
523
602
  }
524
603
 
525
604
  // src/BaseSettingsDialog.tsx
526
- var import_jsx_runtime7 = require("react/jsx-runtime");
605
+ var import_jsx_runtime9 = require("react/jsx-runtime");
527
606
  function SettingIcon({ source, color, containerColor }) {
528
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native8.View, { style: [styles6.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Icon, { source, size: 18, color }) });
607
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: [styles8.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Icon, { source, size: 18, color }) });
529
608
  }
530
- function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, children }) {
531
- const { colors } = (0, import_auto_paper6.useAutoPaperTheme)();
532
- const ambientRotation = (0, import_core2.useRotation)();
609
+ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, onShowHowToPlay, children }) {
610
+ const { colors } = (0, import_auto_paper7.useAutoPaperTheme)();
611
+ const ambientRotation = (0, import_core3.useRotation)();
533
612
  const rotation = rotationOverride ?? ambientRotation;
534
- const isTouchPrimary = (0, import_core2.useIsTouchPrimaryDevice)();
613
+ const isTouchPrimary = (0, import_core3.useIsTouchPrimaryDevice)();
535
614
  const { settings: hapticSettings, set: setHapticSettings } = (0, import_feedback_press2.useHapticSettings)();
536
615
  const { settings: soundSettings, set: setSoundSettings } = (0, import_feedback_press2.useSoundSettings)();
537
- const sound = (0, import_react3.useContext)(import_feedback_press2.SoundContext);
616
+ const sound = (0, import_react4.useContext)(import_feedback_press2.SoundContext);
538
617
  const { forceShort: forceHapticFeedback } = (0, import_feedback_press2.useVibration)();
539
- const [infoMessage, setInfoMessage] = (0, import_react3.useState)(null);
618
+ const [infoMessage, setInfoMessage] = (0, import_react4.useState)(null);
540
619
  const { manifest: updateManifest, onConfirm: onUpdateConfirm, respond: respondToUpdateConfirm } = useUpdateConfirmBridge();
541
620
  const { check, checking, updateReady } = (0, import_updater.useUpdater)({
542
621
  autoCheck: false,
@@ -547,32 +626,32 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
547
626
  });
548
627
  const updateManifestDate = updateManifest ? new Date(updateManifest.createdAt) : null;
549
628
  const showLockOrientation = isTouchPrimary && lockOrientation !== void 0 && onLockOrientationChange !== void 0;
550
- const showEdgeGuard = import_react_native8.Platform.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
551
- const showHaptics = import_react_native8.Platform.OS !== "web" && !hideHaptics;
629
+ const showEdgeGuard = import_react_native10.Platform.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
630
+ const showHaptics = import_react_native10.Platform.OS !== "web" && !hideHaptics;
552
631
  const showSound = !hideSound;
553
632
  const showAppearance = !hideAppearance;
554
- const showUpdateCheck = import_react_native8.Platform.OS !== "web" && !hideUpdateCheck;
555
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
556
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_auto_paper6.Dialog, { visible, onDismiss, style: [styles6.dialog, (0, import_core2.toRotationStyle)(rotation)], children: [
557
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_auto_paper6.Dialog.Title, { children: "Settings" }),
558
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_auto_paper6.Dialog.ScrollArea, { style: styles6.scrollArea, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.ScrollView, { contentContainerStyle: styles6.content, showsVerticalScrollIndicator: false, children: [
559
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles6.toggleGroup, children: [
560
- showLockOrientation && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles6.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles6.toggleContent, children: [
561
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
562
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles6.flexShrink, children: [
563
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
564
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
633
+ const showUpdateCheck = import_react_native10.Platform.OS !== "web" && !hideUpdateCheck;
634
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(RotatedFrame, { rotation, rotatedSafeArea: true, portalHost: true, passThrough: true, children: [
635
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_auto_paper7.Dialog, { visible, onDismiss, style: styles8.dialog, children: [
636
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper7.Dialog.Title, { children: "Settings" }),
637
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper7.Dialog.ScrollArea, { style: styles8.scrollArea, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.ScrollView, { contentContainerStyle: styles8.content, showsVerticalScrollIndicator: false, children: [
638
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleGroup, children: [
639
+ showLockOrientation && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles8.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleContent, children: [
640
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
641
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.flexShrink, children: [
642
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
643
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
565
644
  ] })
566
645
  ] }) }),
567
- showEdgeGuard && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles6.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles6.toggleContent, children: [
568
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
569
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles6.flexShrink, children: [
570
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
571
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
646
+ showEdgeGuard && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles8.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleContent, children: [
647
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.flexShrink, children: [
649
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
650
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
572
651
  ] })
573
652
  ] }) }),
574
- (showSound || showHaptics) && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles6.toggleRow, children: [
575
- showSound && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
653
+ (showSound || showHaptics) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleRow, children: [
654
+ showSound && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
576
655
  import_feedback_press2.TouchableRipple,
577
656
  {
578
657
  soundDisabled: true,
@@ -581,15 +660,15 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
581
660
  setSoundSettings({ enabled });
582
661
  if (enabled) sound.selection?.();
583
662
  },
584
- style: styles6.toggleButtonInRow,
663
+ style: styles8.toggleButtonInRow,
585
664
  accessibilityLabel: `Sound ${soundSettings.enabled ? "on" : "off"}`,
586
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles6.toggleContent, children: [
587
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
588
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
665
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleContent, children: [
666
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
667
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
589
668
  ] })
590
669
  }
591
670
  ),
592
- showHaptics && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
671
+ showHaptics && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
593
672
  import_feedback_press2.TouchableRipple,
594
673
  {
595
674
  hapticDisabled: true,
@@ -598,36 +677,54 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
598
677
  setHapticSettings({ vibrate });
599
678
  if (vibrate) forceHapticFeedback();
600
679
  },
601
- style: styles6.toggleButtonInRow,
680
+ style: styles8.toggleButtonInRow,
602
681
  accessibilityLabel: `Haptics ${hapticSettings.vibrate ? "on" : "off"}`,
603
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles6.toggleContent, children: [
604
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
605
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
682
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleContent, children: [
683
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
684
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
606
685
  ] })
607
686
  }
608
687
  )
609
- ] })
688
+ ] }),
689
+ onShowHowToPlay && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
690
+ import_feedback_press2.TouchableRipple,
691
+ {
692
+ onPress: () => {
693
+ onDismiss();
694
+ onShowHowToPlay();
695
+ },
696
+ style: styles8.toggleButton,
697
+ accessibilityLabel: "How to Play",
698
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.toggleContent, children: [
699
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SettingIcon, { source: "help-circle-outline", color: colors.secondary, containerColor: colors.secondaryContainer }),
700
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.flexShrink, children: [
701
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "How to Play" }),
702
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Replay the quick intro" })
703
+ ] })
704
+ ] })
705
+ }
706
+ )
610
707
  ] }),
611
- showAppearance && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles6.section, children: [
612
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Text, { variant: "labelMedium", style: [styles6.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
613
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_auto_paper6.AutoAppearancePicker, { showLabels: false })
708
+ showAppearance && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.section, children: [
709
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper8.Text, { variant: "labelMedium", style: [styles8.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
710
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper7.AutoAppearancePicker, { showLabels: false })
614
711
  ] }),
615
712
  children,
616
- showUpdateCheck && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles6.section, children: [
617
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native_paper6.Text, { variant: "labelSmall", style: [styles6.sectionLabel, { color: colors.onSurfaceVariant }], children: [
713
+ showUpdateCheck && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: styles8.section, children: [
714
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_paper8.Text, { variant: "labelSmall", style: [styles8.sectionLabel, { color: colors.onSurfaceVariant }], children: [
618
715
  "VERSION ",
619
716
  version,
620
717
  updateReady ? " \xB7 UPDATE READY" : ""
621
718
  ] }),
622
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press2.Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
719
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press2.Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
623
720
  ] })
624
721
  ] }) })
625
722
  ] }),
626
- /* @__PURE__ */ (0, import_jsx_runtime7.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) }),
627
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ConfirmDialog, { visible: !!infoMessage, title: infoMessage?.title ?? "", message: infoMessage?.message ?? "", confirmLabel: "OK", icon: "information-outline", rotation, onConfirm: () => setInfoMessage(null) })
628
- ] });
723
+ /* @__PURE__ */ (0, import_jsx_runtime9.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) }),
724
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ConfirmDialog, { visible: !!infoMessage, title: infoMessage?.title ?? "", message: infoMessage?.message ?? "", confirmLabel: "OK", icon: "information-outline", rotation: 0, onConfirm: () => setInfoMessage(null) })
725
+ ] }) });
629
726
  }
630
- var styles6 = import_react_native8.StyleSheet.create({
727
+ var styles8 = import_react_native10.StyleSheet.create({
631
728
  content: {
632
729
  gap: 24,
633
730
  paddingVertical: 20
@@ -695,31 +792,31 @@ var styles6 = import_react_native8.StyleSheet.create({
695
792
  });
696
793
 
697
794
  // src/BaseStatsScreen.tsx
698
- var import_auto_paper7 = require("@rific/auto-paper");
795
+ var import_auto_paper8 = require("@rific/auto-paper");
699
796
  var import_feedback_press3 = require("@rific/feedback-press");
700
- var import_core3 = require("@tastic/core");
701
- var import_react4 = require("react");
702
- var import_react_native9 = require("react-native");
703
- var import_react_native_paper7 = require("react-native-paper");
704
- var import_jsx_runtime8 = require("react/jsx-runtime");
797
+ var import_core4 = require("@tastic/core");
798
+ var import_react5 = require("react");
799
+ var import_react_native11 = require("react-native");
800
+ var import_react_native_paper9 = require("react-native-paper");
801
+ var import_jsx_runtime10 = require("react/jsx-runtime");
705
802
  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 }) {
706
- const { dark, colors } = (0, import_auto_paper7.useAutoPaperTheme)();
707
- const ambientRotation = (0, import_core3.useRotation)();
803
+ const { dark, colors } = (0, import_auto_paper8.useAutoPaperTheme)();
804
+ const ambientRotation = (0, import_core4.useRotation)();
708
805
  const rotation = rotationOverride ?? ambientRotation;
709
- const [confirmResetVisible, setConfirmResetVisible] = (0, import_react4.useState)(false);
806
+ const [confirmResetVisible, setConfirmResetVisible] = (0, import_react5.useState)(false);
710
807
  const showReset = !!onReset;
711
808
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
712
809
  const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
713
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: [styles7.container, { backgroundColor: bg }], children: [
714
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.View, { style: [styles7.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
715
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
716
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper7.Text, { variant: titleVariant, style: [styles7.title, { color: fg }], children: title })
810
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: [styles9.container, { backgroundColor: bg }], children: [
811
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: [styles9.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
812
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
813
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper9.Text, { variant: titleVariant, style: [styles9.title, { color: fg }], children: title })
717
814
  ] }),
718
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native9.ScrollView, { style: styles7.scrollView, contentContainerStyle: [styles7.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
815
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.ScrollView, { style: styles9.scrollView, contentContainerStyle: [styles9.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
719
816
  children,
720
- showReset && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press3.Button, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles7.resetButton, children: resetLabel })
817
+ showReset && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_feedback_press3.Button, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles9.resetButton, children: resetLabel })
721
818
  ] }),
722
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
819
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
723
820
  ConfirmDialog,
724
821
  {
725
822
  visible: confirmResetVisible,
@@ -739,7 +836,7 @@ function BaseStatsScreen({ rotation: rotationOverride, title = "Achievements", t
739
836
  )
740
837
  ] });
741
838
  }
742
- var styles7 = import_react_native9.StyleSheet.create({
839
+ var styles9 = import_react_native11.StyleSheet.create({
743
840
  container: {
744
841
  flex: 1
745
842
  },
@@ -770,19 +867,19 @@ var styles7 = import_react_native9.StyleSheet.create({
770
867
  });
771
868
 
772
869
  // src/ContentGutter.tsx
773
- var import_core4 = require("@tastic/core");
774
- var import_react_native10 = require("react-native");
775
- var import_jsx_runtime9 = require("react/jsx-runtime");
870
+ var import_core5 = require("@tastic/core");
871
+ var import_react_native12 = require("react-native");
872
+ var import_jsx_runtime11 = require("react/jsx-runtime");
776
873
  function ContentGutter({ maxContentWidth, leftGutter, rightGutter, style, children }) {
777
- const { width: windowWidth } = (0, import_react_native10.useWindowDimensions)();
778
- const { contentWidth, gutterWidth } = (0, import_core4.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
779
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: [styles8.row, style], children: [
780
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: [styles8.gutter, { width: gutterWidth }], children: leftGutter }),
781
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: { width: contentWidth }, children }),
782
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: [styles8.gutter, { width: gutterWidth }], children: rightGutter })
874
+ const { width: windowWidth } = (0, import_core5.useRotatedWindowDimensions)();
875
+ const { contentWidth, gutterWidth } = (0, import_core5.computeContentBounds)(windowWidth, maxContentWidth ?? Infinity);
876
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native12.View, { style: [styles10.row, style], children: [
877
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native12.View, { style: [styles10.gutter, { width: gutterWidth }], children: leftGutter }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native12.View, { style: { width: contentWidth }, children }),
879
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native12.View, { style: [styles10.gutter, { width: gutterWidth }], children: rightGutter })
783
880
  ] });
784
881
  }
785
- var styles8 = import_react_native10.StyleSheet.create({
882
+ var styles10 = import_react_native12.StyleSheet.create({
786
883
  // overflow:'hidden' keeps an oversized leftGutter/rightGutter from bleeding into the content
787
884
  // column instead of clipping cleanly at its own real width.
788
885
  gutter: {
@@ -794,41 +891,41 @@ var styles8 = import_react_native10.StyleSheet.create({
794
891
  });
795
892
 
796
893
  // src/ControlSchemePicker.tsx
797
- var import_core7 = require("@tastic/core");
798
- var import_react_native15 = require("react-native");
894
+ var import_core8 = require("@tastic/core");
895
+ var import_react_native17 = require("react-native");
799
896
 
800
897
  // src/SectionedDropdown.tsx
801
- var import_auto_paper8 = require("@rific/auto-paper");
898
+ var import_auto_paper9 = require("@rific/auto-paper");
802
899
  var import_feedback_press4 = require("@rific/feedback-press");
803
- var import_core6 = require("@tastic/core");
804
- var import_react_native14 = require("react-native");
805
- var import_react_native_paper8 = require("react-native-paper");
900
+ var import_core7 = require("@tastic/core");
901
+ var import_react_native16 = require("react-native");
902
+ var import_react_native_paper10 = require("react-native-paper");
806
903
 
807
904
  // src/PopoverBody.tsx
808
- var import_react_native11 = require("react-native");
809
- var import_jsx_runtime10 = require("react/jsx-runtime");
905
+ var import_react_native13 = require("react-native");
906
+ var import_jsx_runtime12 = require("react/jsx-runtime");
810
907
  var CARET_SIZE = 7;
811
908
  var CARET_RING_OFFSET = 2;
812
909
  var CARET_DIP = 1;
813
910
  function PopoverBody({ visible, children, align = "center", verticalAlign = "below", caretColor, caretBorderColor, caretBorderWidth = 1, triggerSize = 0 }) {
814
911
  if (!visible) return null;
815
912
  const above = verticalAlign === "above";
816
- const alignStyle = align === "left" ? styles9.contentLeft : align === "right" ? styles9.contentRight : styles9.contentCenter;
817
- const verticalStyle = above ? styles9.contentAbove : styles9.contentBelow;
913
+ const alignStyle = align === "left" ? styles11.contentLeft : align === "right" ? styles11.contentRight : styles11.contentCenter;
914
+ const verticalStyle = above ? styles11.contentAbove : styles11.contentBelow;
818
915
  const ringSize = CARET_SIZE + caretBorderWidth;
819
916
  const caretOffset = (halfFootprint) => align === "right" ? { right: triggerSize / 2 - halfFootprint } : { left: triggerSize / 2 - halfFootprint };
820
917
  const caretFill = above ? "borderTopColor" : "borderBottomColor";
821
918
  const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
822
919
  const caretEdge = (size) => above ? { bottom: -size + CARET_DIP } : { top: -size + CARET_DIP };
823
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native11.View, { style: [styles9.content, alignStyle, verticalStyle], children: [
920
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native13.View, { style: [styles11.content, alignStyle, verticalStyle], children: [
824
921
  children,
825
- caretColor && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
826
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native11.View, { style: [styles9.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
827
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native11.View, { style: [styles9.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
922
+ caretColor && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
923
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { style: [styles11.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
924
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { style: [styles11.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
828
925
  ] })
829
926
  ] });
830
927
  }
831
- var styles9 = import_react_native11.StyleSheet.create({
928
+ var styles11 = import_react_native13.StyleSheet.create({
832
929
  caret: {
833
930
  borderLeftColor: "transparent",
834
931
  borderRightColor: "transparent",
@@ -871,11 +968,11 @@ var styles9 = import_react_native11.StyleSheet.create({
871
968
  });
872
969
 
873
970
  // src/SkiaGate.tsx
874
- var import_react5 = require("react");
875
- var import_jsx_runtime11 = require("react/jsx-runtime");
971
+ var import_react6 = require("react");
972
+ var import_jsx_runtime13 = require("react/jsx-runtime");
876
973
  function SkiaGate({ getComponent, componentProps, fallback = null }) {
877
- const [Component, setComponent] = (0, import_react5.useState)(null);
878
- (0, import_react5.useEffect)(() => {
974
+ const [Component, setComponent] = (0, import_react6.useState)(null);
975
+ (0, import_react6.useEffect)(() => {
879
976
  let cancelled = false;
880
977
  getComponent().then((mod) => {
881
978
  if (!cancelled) setComponent(() => mod.default);
@@ -885,19 +982,19 @@ function SkiaGate({ getComponent, componentProps, fallback = null }) {
885
982
  };
886
983
  }, [getComponent]);
887
984
  if (!Component) return fallback;
888
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Component, { ...componentProps });
985
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Component, { ...componentProps });
889
986
  }
890
987
 
891
988
  // src/TriggerGaugeHost.tsx
892
- var import_jsx_runtime13 = require("react/jsx-runtime");
989
+ var import_jsx_runtime15 = require("react/jsx-runtime");
893
990
  var getTriggerGauge = () => Promise.resolve().then(() => (init_TriggerGauge(), TriggerGauge_exports)).then((mod) => ({ default: mod.TriggerGauge }));
894
991
  function TriggerGaugeHost(props) {
895
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
992
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
896
993
  }
897
994
 
898
995
  // src/useAutoAlign.ts
899
- var import_react7 = require("react");
900
- var import_react_native13 = require("react-native");
996
+ var import_react8 = require("react");
997
+ var import_react_native15 = require("react-native");
901
998
  var EDGE_MARGIN = 12;
902
999
  function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
903
1000
  if (rotation === 0) return { x, y, width, height };
@@ -909,18 +1006,18 @@ function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
909
1006
  return { x: cx + newDx - newWidth / 2, y: cy + newDy - newHeight / 2, width: newWidth, height: newHeight };
910
1007
  }
911
1008
  function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
912
- const triggerRef = (0, import_react7.useRef)(null);
913
- const { width: windowWidth, height: windowHeight } = (0, import_react_native13.useWindowDimensions)();
914
- const [align, setAlign] = (0, import_react7.useState)("center");
915
- const [verticalAlign, setVerticalAlign] = (0, import_react7.useState)("below");
916
- const [maxHeight, setMaxHeight] = (0, import_react7.useState)(contentHeight);
917
- const [measured, setMeasured] = (0, import_react7.useState)(false);
918
- const [prevOpen, setPrevOpen] = (0, import_react7.useState)(open);
1009
+ const triggerRef = (0, import_react8.useRef)(null);
1010
+ const { width: windowWidth, height: windowHeight } = (0, import_react_native15.useWindowDimensions)();
1011
+ const [align, setAlign] = (0, import_react8.useState)("center");
1012
+ const [verticalAlign, setVerticalAlign] = (0, import_react8.useState)("below");
1013
+ const [maxHeight, setMaxHeight] = (0, import_react8.useState)(contentHeight);
1014
+ const [measured, setMeasured] = (0, import_react8.useState)(false);
1015
+ const [prevOpen, setPrevOpen] = (0, import_react8.useState)(open);
919
1016
  if (open !== prevOpen) {
920
1017
  setPrevOpen(open);
921
1018
  if (open) setMeasured(false);
922
1019
  }
923
- const measure = (0, import_react7.useCallback)(() => {
1020
+ const measure = (0, import_react8.useCallback)(() => {
924
1021
  triggerRef.current?.measureInWindow((rawX, rawY, rawWidth, rawHeight) => {
925
1022
  const { x, y, width: triggerWidth, height: triggerHeight } = rotateRect(rawX, rawY, rawWidth, rawHeight, rotation, windowWidth, windowHeight);
926
1023
  const centerX = x + triggerWidth / 2;
@@ -937,14 +1034,14 @@ function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
937
1034
  setMeasured(true);
938
1035
  });
939
1036
  }, [contentWidth, contentHeight, windowWidth, windowHeight, rotation]);
940
- (0, import_react7.useEffect)(() => {
1037
+ (0, import_react8.useEffect)(() => {
941
1038
  if (open) measure();
942
1039
  }, [open, measure]);
943
1040
  return { align, maxHeight, measured, triggerRef, verticalAlign };
944
1041
  }
945
1042
 
946
1043
  // src/SectionedDropdown.tsx
947
- var import_jsx_runtime14 = require("react/jsx-runtime");
1044
+ var import_jsx_runtime16 = require("react/jsx-runtime");
948
1045
  var MENU_BORDER_WIDTH = 1;
949
1046
  var TRIGGER_SIZE = 44;
950
1047
  var MENU_MIN_WIDTH = 200;
@@ -956,11 +1053,11 @@ var MENU_ITEM_HEIGHT_WITH_DESCRIPTION = 58;
956
1053
  var MENU_DIVIDER_HEIGHT = 9;
957
1054
  var MENU_VERTICAL_PADDING = 12;
958
1055
  function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accentColor, mutedColor, onAccentColor, dark, align: forcedAlign, alignOverride, rotation: rotationOverride, autoDismiss = true, labelFontFamily = MONO_FONT, allClearLabels = { all: "All", clear: "Clear" } }) {
959
- const ambientRotation = (0, import_core6.useRotation)();
1056
+ const ambientRotation = (0, import_core7.useRotation)();
960
1057
  const rotation = rotationOverride ?? ambientRotation;
961
1058
  const menuBg = dark ? "#000000" : "#FFFFFF";
962
- const menuBorderColor = (0, import_auto_paper8.getBlendedColor)(mutedColor, menuBg, 0.5);
963
- const resolvedOnAccentColor = onAccentColor ?? (0, import_auto_paper8.getColorRoles)(accentColor, menuBg).onColor;
1059
+ const menuBorderColor = (0, import_auto_paper9.getBlendedColor)(mutedColor, menuBg, 0.5);
1060
+ const resolvedOnAccentColor = onAccentColor ?? (0, import_auto_paper9.getColorRoles)(accentColor, menuBg).onColor;
964
1061
  const fg = dark ? "#FFFFFF" : "#000000";
965
1062
  const open = host.openId === id;
966
1063
  const estimatedHeight = MENU_VERTICAL_PADDING + sections.reduce((sum, section, sectionIndex) => {
@@ -994,17 +1091,17 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
994
1091
  }
995
1092
  gaugeOffset += section.options.length;
996
1093
  }
997
- const anchorStyle = [styles11.anchor, open && styles11.anchorOpen];
1094
+ const anchorStyle = [styles13.anchor, open && styles13.anchorOpen];
998
1095
  const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
999
1096
  const dividerStyle = { backgroundColor: menuBorderColor };
1000
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native14.View, { style: anchorStyle, children: [
1001
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native14.View, { ref: triggerRef, collapsable: false, style: styles11.triggerBox, children: [
1002
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
1003
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press4.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
1097
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native16.View, { style: anchorStyle, children: [
1098
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native16.View, { ref: triggerRef, collapsable: false, style: styles13.triggerBox, children: [
1099
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
1100
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_feedback_press4.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
1004
1101
  ] }),
1005
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native14.ScrollView, { style: [styles11.menu, menuStyle], contentContainerStyle: styles11.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native14.View, { children: [
1006
- sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native14.View, { style: [styles11.divider, dividerStyle] }),
1007
- section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1102
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native16.ScrollView, { style: [styles13.menu, menuStyle], contentContainerStyle: styles13.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native16.View, { children: [
1103
+ sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native16.View, { style: [styles13.divider, dividerStyle] }),
1104
+ section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1008
1105
  SingleSection,
1009
1106
  {
1010
1107
  section,
@@ -1017,7 +1114,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
1017
1114
  if (autoDismiss) host.close();
1018
1115
  }
1019
1116
  }
1020
- ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
1117
+ ) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
1021
1118
  ] }, section.id)) }) })
1022
1119
  ] });
1023
1120
  }
@@ -1029,18 +1126,18 @@ function SingleSection({
1029
1126
  labelFontFamily,
1030
1127
  onSelect
1031
1128
  }) {
1032
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: section.options.map((option) => {
1129
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: section.options.map((option) => {
1033
1130
  const selected = option.value === section.value;
1034
1131
  const taken = !selected && option.value === section.takenValue;
1035
1132
  const rowColor = selected ? onAccentColor : mutedColor;
1036
- const itemStyle = [styles11.item, selected && { backgroundColor: accentColor }, taken && styles11.itemTaken];
1133
+ const itemStyle = [styles13.item, selected && { backgroundColor: accentColor }, taken && styles13.itemTaken];
1037
1134
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
1038
1135
  const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
1039
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press4.TouchableRipple, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native14.View, { style: styles11.itemRow, children: [
1040
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1041
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native14.View, { style: styles11.itemLabelColumn, children: [
1042
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
1043
- option.description && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
1136
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_feedback_press4.TouchableRipple, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native16.View, { style: styles13.itemRow, children: [
1137
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1138
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native16.View, { style: styles13.itemLabelColumn, children: [
1139
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
1140
+ option.description && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
1044
1141
  ] })
1045
1142
  ] }) }, option.value);
1046
1143
  }) });
@@ -1054,23 +1151,23 @@ function MultiSection({
1054
1151
  allClearLabels
1055
1152
  }) {
1056
1153
  const allSelected = section.value.length === section.options.length;
1057
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
1154
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
1058
1155
  section.options.map((option, i) => {
1059
1156
  const selected = section.value.includes(option.value);
1060
1157
  const rowColor = selected ? onAccentColor : mutedColor;
1061
1158
  const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
1062
1159
  const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
1063
- const itemStyle = [styles11.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
1160
+ const itemStyle = [styles13.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
1064
1161
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
1065
- return /* @__PURE__ */ (0, import_jsx_runtime14.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_runtime14.jsxs)(import_react_native14.View, { style: styles11.itemRow, children: [
1066
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1067
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Text, { variant: "labelLarge", style: [styles11.itemLabelFlex, labelStyle], children: option.label })
1162
+ return /* @__PURE__ */ (0, import_jsx_runtime16.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_runtime16.jsxs)(import_react_native16.View, { style: styles13.itemRow, children: [
1163
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
1164
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Text, { variant: "labelLarge", style: [styles13.itemLabelFlex, labelStyle], children: option.label })
1068
1165
  ] }) }, option.value);
1069
1166
  }),
1070
- section.allClear && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles11.item, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper8.Text, { variant: "labelLarge", style: [styles11.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
1167
+ section.allClear && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_feedback_press4.TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles13.item, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper10.Text, { variant: "labelLarge", style: [styles13.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
1071
1168
  ] });
1072
1169
  }
1073
- var styles11 = import_react_native14.StyleSheet.create({
1170
+ var styles13 = import_react_native16.StyleSheet.create({
1074
1171
  allClearLabel: {
1075
1172
  fontWeight: "bold",
1076
1173
  textAlign: "center"
@@ -1087,7 +1184,7 @@ var styles11 = import_react_native14.StyleSheet.create({
1087
1184
  zIndex: 100
1088
1185
  },
1089
1186
  divider: {
1090
- height: import_react_native14.StyleSheet.hairlineWidth,
1187
+ height: import_react_native16.StyleSheet.hairlineWidth,
1091
1188
  marginVertical: 4
1092
1189
  },
1093
1190
  item: {
@@ -1140,24 +1237,24 @@ var styles11 = import_react_native14.StyleSheet.create({
1140
1237
  });
1141
1238
 
1142
1239
  // src/ControlSchemePicker.tsx
1143
- var import_jsx_runtime15 = require("react/jsx-runtime");
1240
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1144
1241
  function ControlSchemePicker({ id, host, value, onChange, options, takenValue, accentColor, mutedColor, onAccentColor, dark, align, alignOverride, rotation, labelFontFamily }) {
1145
- const isTouchPrimary = (0, import_core7.useIsTouchPrimaryDevice)();
1146
- if (import_react_native15.Platform.OS !== "web" || isTouchPrimary) return null;
1147
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SectionedDropdown, { id, host, icon: "keyboard-outline", accessibilityLabel: "Control scheme", sections: [{ kind: "single", id: "controls", options, value, onChange, takenValue }], accentColor, mutedColor, onAccentColor, dark, align, alignOverride, rotation, labelFontFamily });
1242
+ const isTouchPrimary = (0, import_core8.useIsTouchPrimaryDevice)();
1243
+ if (import_react_native17.Platform.OS !== "web" || isTouchPrimary) return null;
1244
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SectionedDropdown, { id, host, icon: "keyboard-outline", accessibilityLabel: "Control scheme", sections: [{ kind: "single", id: "controls", options, value, onChange, takenValue }], accentColor, mutedColor, onAccentColor, dark, align, alignOverride, rotation, labelFontFamily });
1148
1245
  }
1149
1246
 
1150
1247
  // src/CornerActionButtons.tsx
1151
1248
  var import_feedback_press5 = require("@rific/feedback-press");
1152
- var import_react_native16 = require("react-native");
1153
- var import_jsx_runtime16 = require("react/jsx-runtime");
1249
+ var import_react_native18 = require("react-native");
1250
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1154
1251
  function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon = "arrow-left", backLabel = "Back" }) {
1155
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
1156
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_feedback_press5.IconButton, { icon: backIcon, iconColor: fg, size: 24, style: [styles12.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: backLabel }),
1157
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_feedback_press5.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles12.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
1252
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
1253
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press5.IconButton, { icon: backIcon, iconColor: fg, size: 24, style: [styles14.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: backLabel }),
1254
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press5.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles14.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
1158
1255
  ] });
1159
1256
  }
1160
- var styles12 = import_react_native16.StyleSheet.create({
1257
+ var styles14 = import_react_native18.StyleSheet.create({
1161
1258
  back: {
1162
1259
  position: "absolute"
1163
1260
  },
@@ -1167,22 +1264,22 @@ var styles12 = import_react_native16.StyleSheet.create({
1167
1264
  });
1168
1265
 
1169
1266
  // src/CornerStatusBadges.tsx
1170
- var import_react_native17 = require("react-native");
1171
- var import_react_native_paper9 = require("react-native-paper");
1172
- var import_jsx_runtime17 = require("react/jsx-runtime");
1267
+ var import_react_native19 = require("react-native");
1268
+ var import_react_native_paper11 = require("react-native-paper");
1269
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1173
1270
  var BADGE_SIZE = 30;
1174
1271
  function StatusBadge({ icon }) {
1175
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native17.View, { style: styles13.badge, children: icon && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native_paper9.Icon, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
1272
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native19.View, { style: styles15.badge, children: icon && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Icon, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
1176
1273
  }
1177
1274
  function CornerStatusBadges({ orientationMode, p1OnRight, p1, p2 }) {
1178
1275
  const p1OnRightSide = orientationMode === "sideBySide" && p1OnRight;
1179
1276
  const p2OnLeftSide = orientationMode === "sideBySide" && !p1OnRight;
1180
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
1181
- p1 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native17.View, { pointerEvents: "none", style: p1OnRightSide ? styles13.bottomRight : styles13.bottomLeft, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(StatusBadge, { icon: p1.icon }) }),
1182
- p2 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native17.View, { pointerEvents: "none", style: p2OnLeftSide ? styles13.topLeft : styles13.topRight, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(StatusBadge, { icon: p2.icon }) })
1277
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
1278
+ p1 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native19.View, { pointerEvents: "none", style: p1OnRightSide ? styles15.bottomRight : styles15.bottomLeft, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StatusBadge, { icon: p1.icon }) }),
1279
+ p2 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native19.View, { pointerEvents: "none", style: p2OnLeftSide ? styles15.topLeft : styles15.topRight, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StatusBadge, { icon: p2.icon }) })
1183
1280
  ] });
1184
1281
  }
1185
- var styles13 = import_react_native17.StyleSheet.create({
1282
+ var styles15 = import_react_native19.StyleSheet.create({
1186
1283
  badge: {
1187
1284
  alignItems: "center",
1188
1285
  backgroundColor: "rgba(0,0,0,0.28)",
@@ -1201,12 +1298,12 @@ var styles13 = import_react_native17.StyleSheet.create({
1201
1298
  });
1202
1299
 
1203
1300
  // src/InlineColorPicker.tsx
1204
- var import_auto_paper9 = require("@rific/auto-paper");
1301
+ var import_auto_paper10 = require("@rific/auto-paper");
1205
1302
  var import_feedback_press6 = require("@rific/feedback-press");
1206
- var import_core8 = require("@tastic/core");
1207
- var import_react_native18 = require("react-native");
1208
- var import_react_native_paper10 = require("react-native-paper");
1209
- var import_jsx_runtime18 = require("react/jsx-runtime");
1303
+ var import_core9 = require("@tastic/core");
1304
+ var import_react_native20 = require("react-native");
1305
+ var import_react_native_paper12 = require("react-native-paper");
1306
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1210
1307
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
1211
1308
  var DEFAULT_SIZE = 48;
1212
1309
  var SWATCH_SIZE = 28;
@@ -1220,18 +1317,18 @@ function widthForColumns(columns) {
1220
1317
  return SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * columns + SWATCHES_GAP * (columns - 1);
1221
1318
  }
1222
1319
  function getInlineColorPickerContentSize(swatchCount, windowWidth, columns) {
1223
- const autoColumns = (0, import_core8.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
1320
+ const autoColumns = (0, import_core9.clamp)(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
1224
1321
  const resolvedColumns = columns ?? autoColumns;
1225
1322
  const width = widthForColumns(resolvedColumns);
1226
1323
  const rows = Math.ceil(swatchCount / resolvedColumns);
1227
1324
  const height = SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * rows + SWATCHES_GAP * (rows - 1);
1228
1325
  return { width, height };
1229
1326
  }
1230
- function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = import_auto_paper9.defaultColors, takenValue, allowSwapTaken, dark, align: forcedAlign, alignOverride, rotation: rotationOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
1231
- const ambientRotation = (0, import_core8.useRotation)();
1327
+ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = import_auto_paper10.defaultColors, takenValue, allowSwapTaken, dark, align: forcedAlign, alignOverride, rotation: rotationOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
1328
+ const ambientRotation = (0, import_core9.useRotation)();
1232
1329
  const rotation = rotationOverride ?? ambientRotation;
1233
1330
  const menuBg = dark ? "#000000" : "#FFFFFF";
1234
- const { width: windowWidth } = (0, import_react_native18.useWindowDimensions)();
1331
+ const { width: windowWidth } = (0, import_react_native20.useWindowDimensions)();
1235
1332
  const { width: swatchesWidth, height: swatchesHeight } = getInlineColorPickerContentSize(swatches.length, windowWidth, columns);
1236
1333
  const open = host.openId === id;
1237
1334
  const auto = useAutoAlign(open, swatchesWidth, swatchesHeight, rotation);
@@ -1240,16 +1337,16 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1240
1337
  const align = forcedAlign ?? placement.align;
1241
1338
  const displayFor = previewValue ?? ((hex) => hex);
1242
1339
  const displayValue = displayFor(value);
1243
- const contrastColor = (0, import_auto_paper9.getContrastColor)(displayValue);
1340
+ const contrastColor = (0, import_auto_paper10.getContrastColor)(displayValue);
1244
1341
  const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
1245
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native18.View, { style: [styles14.anchor, open && styles14.anchorOpen], children: [
1246
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press6.TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles14.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native18.View, { ref: triggerRef, collapsable: false, style: styles14.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native_paper10.Text, { style: [styles14.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native_paper10.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
1247
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native18.ScrollView, { style: [styles14.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles14.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
1342
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native20.View, { style: [styles16.anchor, open && styles16.anchorOpen], children: [
1343
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_feedback_press6.TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles16.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native20.View, { ref: triggerRef, collapsable: false, style: styles16.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Text, { style: [styles16.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
1344
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native20.ScrollView, { style: [styles16.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles16.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
1248
1345
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
1249
1346
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
1250
1347
  const swappable = taken && allowSwapTaken;
1251
1348
  const swatchDisplay = displayFor(swatch.value);
1252
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1349
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1253
1350
  import_feedback_press6.TouchableRipple,
1254
1351
  {
1255
1352
  disabled: taken && !swappable,
@@ -1258,15 +1355,15 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1258
1355
  if (autoDismiss) host.close();
1259
1356
  },
1260
1357
  borderless: true,
1261
- style: [styles14.swatch, { backgroundColor: swatchDisplay }, selected && styles14.swatchSelected, taken && !swappable && styles14.swatchTaken],
1262
- children: selected ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native_paper10.Icon, { source: "check", size: 16, color: (0, import_auto_paper9.getContrastColor)(swatchDisplay) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native_paper10.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper9.getContrastColor)(swatchDisplay) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native_paper10.Icon, { source: "close", size: 16, color: (0, import_auto_paper9.getContrastColor)(swatchDisplay) }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native18.View, {})
1358
+ style: [styles16.swatch, { backgroundColor: swatchDisplay }, selected && styles16.swatchSelected, taken && !swappable && styles16.swatchTaken],
1359
+ children: selected ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Icon, { source: "check", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Icon, { source: "close", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native20.View, {})
1263
1360
  },
1264
1361
  swatch.value
1265
1362
  );
1266
1363
  }) }) })
1267
1364
  ] });
1268
1365
  }
1269
- var styles14 = import_react_native18.StyleSheet.create({
1366
+ var styles16 = import_react_native20.StyleSheet.create({
1270
1367
  anchor: {
1271
1368
  position: "relative"
1272
1369
  },
@@ -1338,11 +1435,11 @@ var styles14 = import_react_native18.StyleSheet.create({
1338
1435
  });
1339
1436
 
1340
1437
  // src/LabeledDropdown.tsx
1341
- var import_auto_paper10 = require("@rific/auto-paper");
1438
+ var import_auto_paper11 = require("@rific/auto-paper");
1342
1439
  var import_feedback_press7 = require("@rific/feedback-press");
1343
- var import_react_native19 = require("react-native");
1344
- var import_react_native_paper11 = require("react-native-paper");
1345
- var import_jsx_runtime19 = require("react/jsx-runtime");
1440
+ var import_react_native21 = require("react-native");
1441
+ var import_react_native_paper13 = require("react-native-paper");
1442
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1346
1443
  var TRIGGER_HEIGHT = 28;
1347
1444
  var POPOVER_WIDTH = 180;
1348
1445
  var ROW_HEIGHT = 36;
@@ -1365,22 +1462,22 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
1365
1462
  onChange(option.value);
1366
1463
  host.close();
1367
1464
  };
1368
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native19.View, { style: [styles15.anchor, open && styles15.anchorOpen], children: [
1369
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => host.toggle(id), style: styles15.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native19.View, { ref: triggerRef, collapsable: false, style: styles15.triggerInner, children: [
1370
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Text, { style: [styles15.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
1371
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Icon, { source: "menu-down", size: 16, color })
1465
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react_native21.View, { style: [styles17.anchor, open && styles17.anchorOpen], children: [
1466
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => host.toggle(id), style: styles17.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react_native21.View, { ref: triggerRef, collapsable: false, style: styles17.triggerInner, children: [
1467
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native_paper13.Text, { style: [styles17.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
1468
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native_paper13.Icon, { source: "menu-down", size: 16, color })
1372
1469
  ] }) }),
1373
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native19.ScrollView, { style: [styles15.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles15.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
1470
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native21.ScrollView, { style: [styles17.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles17.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
1374
1471
  const isSelected = option.value === value;
1375
- const onColor = (0, import_auto_paper10.getContrastColor)(color);
1376
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => handleSelect(option), style: [styles15.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native19.View, { style: styles15.rowInner, children: [
1377
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
1378
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native_paper11.Text, { style: [styles15.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
1472
+ const onColor = (0, import_auto_paper11.getContrastColor)(color);
1473
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => handleSelect(option), style: [styles17.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react_native21.View, { style: styles17.rowInner, children: [
1474
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native_paper13.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
1475
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native_paper13.Text, { style: [styles17.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
1379
1476
  ] }) }, option.value);
1380
1477
  }) }) })
1381
1478
  ] });
1382
1479
  }
1383
- var styles15 = import_react_native19.StyleSheet.create({
1480
+ var styles17 = import_react_native21.StyleSheet.create({
1384
1481
  anchor: {
1385
1482
  position: "relative"
1386
1483
  },
@@ -1438,22 +1535,22 @@ function loadSkiaWeb() {
1438
1535
  }
1439
1536
 
1440
1537
  // src/PlayerSetupPanel.tsx
1441
- var import_profile = require("@tastic/profile");
1442
- var import_react_native21 = require("react-native");
1443
- var import_react_native_paper13 = require("react-native-paper");
1538
+ var import_profile2 = require("@tastic/profile");
1539
+ var import_react_native23 = require("react-native");
1540
+ var import_react_native_paper15 = require("react-native-paper");
1444
1541
 
1445
1542
  // src/ReadyButton.tsx
1446
1543
  var import_feedback_press8 = require("@rific/feedback-press");
1447
- var import_react_native20 = require("react-native");
1448
- var import_react_native_paper12 = require("react-native-paper");
1449
- var import_jsx_runtime20 = require("react/jsx-runtime");
1544
+ var import_react_native22 = require("react-native");
1545
+ var import_react_native_paper14 = require("react-native-paper");
1546
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1450
1547
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
1451
- const readyButtonStyle = [styles16.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
1548
+ const readyButtonStyle = [styles18.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
1452
1549
  const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
1453
1550
  const labelColorStyle = { color: ready ? "#000000" : color };
1454
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_feedback_press8.TouchableRipple, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native_paper12.Text, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
1551
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_feedback_press8.TouchableRipple, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native_paper14.Text, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
1455
1552
  }
1456
- var styles16 = import_react_native20.StyleSheet.create({
1553
+ var styles18 = import_react_native22.StyleSheet.create({
1457
1554
  readyButton: {
1458
1555
  alignItems: "center",
1459
1556
  borderRadius: 12,
@@ -1466,18 +1563,18 @@ var styles16 = import_react_native20.StyleSheet.create({
1466
1563
  });
1467
1564
 
1468
1565
  // src/usePopoverHost.ts
1469
- var import_react8 = require("react");
1566
+ var import_react9 = require("react");
1470
1567
  function usePopoverHost() {
1471
- const [openId, setOpenId] = (0, import_react8.useState)(null);
1472
- const toggle = (0, import_react8.useCallback)((id) => {
1568
+ const [openId, setOpenId] = (0, import_react9.useState)(null);
1569
+ const toggle = (0, import_react9.useCallback)((id) => {
1473
1570
  setOpenId((prev) => prev === id ? null : id);
1474
1571
  }, []);
1475
- const close = (0, import_react8.useCallback)(() => setOpenId(null), []);
1572
+ const close = (0, import_react9.useCallback)(() => setOpenId(null), []);
1476
1573
  return { openId, toggle, close };
1477
1574
  }
1478
1575
 
1479
1576
  // src/PlayerSetupPanel.tsx
1480
- var import_jsx_runtime21 = require("react/jsx-runtime");
1577
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1481
1578
  function getColorPopoverId(idPrefix) {
1482
1579
  return `${idPrefix}-color`;
1483
1580
  }
@@ -1492,16 +1589,16 @@ function PlayerSetupPanel({ idPrefix, host, label, labelFontFamily, color, onCol
1492
1589
  const showProfilePicker = isHuman && profiles !== void 0 && !!onProfileSelect;
1493
1590
  const showCpuDifficulty = !isHuman && cpuDifficulty !== void 0 && cpuDifficultyOptions !== void 0 && !!onCpuDifficultyChange;
1494
1591
  const selectedProfile = profiles?.find((p) => p.id === selectedProfileId) ?? null;
1495
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react_native21.View, { style: [styles17.panel, ownPopoverOpen && styles17.panelOpen], children: [
1496
- showProfilePicker ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_profile.ProfilePicker, { idPrefix, host: popover, profiles, selectedId: selectedProfileId ?? null, takenId: takenProfileId, color, dark, guestLabel: guestLabel ?? "GUEST", onSelect: (profile) => onProfileSelect?.(profile), onManage: onManageProfiles }) : showCpuDifficulty ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LabeledDropdown, { id: getCpuDifficultyPopoverId(idPrefix), host: popover, options: cpuDifficultyOptions, value: cpuDifficulty, onChange: onCpuDifficultyChange, color, dark, alignOverride: cpuDifficultyAlignOverride }) : label && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native_paper13.Text, { variant: "labelSmall", style: { color, fontFamily: labelFontFamily }, children: label }),
1497
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react_native21.View, { style: [styles17.pickerRow, pickerRowPopoverOpen && styles17.pickerRowOpen], children: [
1498
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InlineColorPicker, { id: getColorPopoverId(idPrefix), host: popover, value: color, onChange: onColorChange, swatches, takenValue: takenColor, allowSwapTaken, alignOverride: colorAlignOverride, rotation: colorRotation, dark, tag: isHuman ? selectedProfile?.tag : void 0, icon: isHuman ? humanIcon : cpuIcon }),
1592
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_native23.View, { style: [styles19.panel, ownPopoverOpen && styles19.panelOpen], children: [
1593
+ showProfilePicker ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_profile2.ProfilePicker, { idPrefix, host: popover, profiles, selectedId: selectedProfileId ?? null, takenId: takenProfileId, color, dark, guestLabel: guestLabel ?? "GUEST", onSelect: (profile) => onProfileSelect?.(profile), onManage: onManageProfiles }) : showCpuDifficulty ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(LabeledDropdown, { id: getCpuDifficultyPopoverId(idPrefix), host: popover, options: cpuDifficultyOptions, value: cpuDifficulty, onChange: onCpuDifficultyChange, color, dark, alignOverride: cpuDifficultyAlignOverride }) : label && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native_paper15.Text, { variant: "labelSmall", style: { color, fontFamily: labelFontFamily }, children: label }),
1594
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_native23.View, { style: [styles19.pickerRow, pickerRowPopoverOpen && styles19.pickerRowOpen], children: [
1595
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(InlineColorPicker, { id: getColorPopoverId(idPrefix), host: popover, value: color, onChange: onColorChange, swatches, takenValue: takenColor, allowSwapTaken, alignOverride: colorAlignOverride, rotation: colorRotation, dark, tag: isHuman ? selectedProfile?.tag : void 0, icon: isHuman ? humanIcon : cpuIcon }),
1499
1596
  secondPicker
1500
1597
  ] }),
1501
- isHuman && showReadyButton && onToggleReady && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ReadyButton, { color, ready: ready ?? false, onToggleReady })
1598
+ isHuman && showReadyButton && onToggleReady && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ReadyButton, { color, ready: ready ?? false, onToggleReady })
1502
1599
  ] });
1503
1600
  }
1504
- var styles17 = import_react_native21.StyleSheet.create({
1601
+ var styles19 = import_react_native23.StyleSheet.create({
1505
1602
  panel: {
1506
1603
  alignItems: "center",
1507
1604
  gap: 12
@@ -1533,29 +1630,29 @@ var styles17 = import_react_native21.StyleSheet.create({
1533
1630
  });
1534
1631
 
1535
1632
  // src/PressAwayOverlay.tsx
1536
- var import_react_native22 = require("react-native");
1537
- var import_jsx_runtime22 = require("react/jsx-runtime");
1633
+ var import_react_native24 = require("react-native");
1634
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1538
1635
  function PressAwayOverlay({ active, onPress, style }) {
1539
1636
  if (!active) return null;
1540
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native22.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native22.StyleSheet.absoluteFill, style] });
1637
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react_native24.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native24.StyleSheet.absoluteFill, style] });
1541
1638
  }
1542
1639
 
1543
1640
  // src/SharedActionBand.tsx
1544
1641
  var import_feedback_press9 = require("@rific/feedback-press");
1545
- var import_react_native23 = require("react-native");
1546
- var import_jsx_runtime23 = require("react/jsx-runtime");
1642
+ var import_react_native25 = require("react-native");
1643
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1547
1644
  function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
1548
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_native23.View, { style: [styles18.column, popoverOpen && styles18.columnOpen], children: [
1549
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_native23.View, { style: styles18.actionsRow, children: [
1550
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_feedback_press9.IconButton, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1551
- onRandomize && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_feedback_press9.IconButton, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1552
- onReset && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_feedback_press9.IconButton, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
1553
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_feedback_press9.IconButton, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
1645
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_native25.View, { style: [styles20.column, popoverOpen && styles20.columnOpen], children: [
1646
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_native25.View, { style: styles20.actionsRow, children: [
1647
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_feedback_press9.IconButton, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1648
+ onRandomize && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_feedback_press9.IconButton, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1649
+ onReset && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_feedback_press9.IconButton, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
1650
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_feedback_press9.IconButton, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
1554
1651
  ] }),
1555
1652
  children
1556
1653
  ] });
1557
1654
  }
1558
- var styles18 = import_react_native23.StyleSheet.create({
1655
+ var styles20 = import_react_native25.StyleSheet.create({
1559
1656
  actionsRow: {
1560
1657
  alignItems: "center",
1561
1658
  flexDirection: "row",
@@ -1571,10 +1668,10 @@ var styles18 = import_react_native23.StyleSheet.create({
1571
1668
  });
1572
1669
 
1573
1670
  // src/StaggeredWord.tsx
1574
- var import_react9 = require("react");
1575
- var import_react_native24 = require("react-native");
1671
+ var import_react10 = require("react");
1672
+ var import_react_native26 = require("react-native");
1576
1673
  var import_react_native_reanimated2 = __toESM(require("react-native-reanimated"));
1577
- var import_jsx_runtime24 = require("react/jsx-runtime");
1674
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1578
1675
  var STAGGER_MS = 45;
1579
1676
  var LETTER_DURATION_MS = 320;
1580
1677
  function getStaggeredWordDuration(letterCount) {
@@ -1582,7 +1679,7 @@ function getStaggeredWordDuration(letterCount) {
1582
1679
  }
1583
1680
  function AnimatedLetter({ char, index, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }) {
1584
1681
  const progress = (0, import_react_native_reanimated2.useSharedValue)(reducedMotion ? 1 : 0);
1585
- (0, import_react9.useEffect)(() => {
1682
+ (0, import_react10.useEffect)(() => {
1586
1683
  if (reducedMotion) return;
1587
1684
  progress.value = (0, import_react_native_reanimated2.withDelay)(index * STAGGER_MS, (0, import_react_native_reanimated2.withTiming)(1, { duration: LETTER_DURATION_MS, easing: import_react_native_reanimated2.Easing.out(import_react_native_reanimated2.Easing.back(1.5)) }));
1588
1685
  }, [reducedMotion]);
@@ -1590,12 +1687,12 @@ function AnimatedLetter({ char, index, color, reducedMotion, fontFamily, fontSiz
1590
1687
  opacity: progress.value,
1591
1688
  transform: [{ translateY: (1 - progress.value) * 12 }, { scale: 0.85 + progress.value * 0.15 }]
1592
1689
  }));
1593
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react_native_reanimated2.default.Text, { allowFontScaling: false, style: [{ color, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, style], children: char });
1690
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native_reanimated2.default.Text, { allowFontScaling: false, style: [{ color, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, style], children: char });
1594
1691
  }
1595
1692
  function StaggeredWord({ word, color, fontFamily, fontSize, lineHeight, reducedMotion, startIndex = 0, fontWeight = "bold", letterSpacing, style, onLayout }) {
1596
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react_native24.View, { style: [styles19.row, style], onLayout, children: word.split("").map((char, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AnimatedLetter, { char, index: startIndex + i, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, i)) });
1693
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native26.View, { style: [styles21.row, style], onLayout, children: word.split("").map((char, i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AnimatedLetter, { char, index: startIndex + i, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, i)) });
1597
1694
  }
1598
- var styles19 = import_react_native24.StyleSheet.create({
1695
+ var styles21 = import_react_native26.StyleSheet.create({
1599
1696
  row: {
1600
1697
  flexDirection: "row"
1601
1698
  }
@@ -1603,53 +1700,53 @@ var styles19 = import_react_native24.StyleSheet.create({
1603
1700
 
1604
1701
  // src/UpdateDialog.tsx
1605
1702
  var import_updater2 = require("@rific/updater");
1606
- var import_jsx_runtime25 = require("react/jsx-runtime");
1703
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1607
1704
  function UpdateDialog({ rotation = 0, autoCheck = true, autoPrompt = true, onError }) {
1608
1705
  const { manifest, onConfirm, respond } = useUpdateConfirmBridge();
1609
1706
  (0, import_updater2.useUpdater)({ autoCheck, autoPrompt, onConfirm, onError });
1610
1707
  const date = manifest ? new Date(manifest.createdAt) : null;
1611
- return /* @__PURE__ */ (0, import_jsx_runtime25.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) });
1708
+ return /* @__PURE__ */ (0, import_jsx_runtime27.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) });
1612
1709
  }
1613
1710
 
1614
1711
  // src/useQuitConfirmation.ts
1615
- var import_react10 = require("react");
1712
+ var import_react11 = require("react");
1616
1713
  function useQuitConfirmation(hasProgress, onConfirmedBack) {
1617
- const [confirmVisible, setConfirmVisible] = (0, import_react10.useState)(false);
1618
- const hasProgressRef = (0, import_react10.useRef)(hasProgress);
1619
- (0, import_react10.useEffect)(() => {
1714
+ const [confirmVisible, setConfirmVisible] = (0, import_react11.useState)(false);
1715
+ const hasProgressRef = (0, import_react11.useRef)(hasProgress);
1716
+ (0, import_react11.useEffect)(() => {
1620
1717
  hasProgressRef.current = hasProgress;
1621
1718
  });
1622
- const onConfirmedBackRef = (0, import_react10.useRef)(onConfirmedBack);
1623
- (0, import_react10.useEffect)(() => {
1719
+ const onConfirmedBackRef = (0, import_react11.useRef)(onConfirmedBack);
1720
+ (0, import_react11.useEffect)(() => {
1624
1721
  onConfirmedBackRef.current = onConfirmedBack;
1625
1722
  });
1626
- const requestBack = (0, import_react10.useCallback)(() => {
1723
+ const requestBack = (0, import_react11.useCallback)(() => {
1627
1724
  if (hasProgressRef.current()) setConfirmVisible(true);
1628
1725
  else onConfirmedBackRef.current();
1629
1726
  }, []);
1630
- const cancelBack = (0, import_react10.useCallback)(() => setConfirmVisible(false), []);
1727
+ const cancelBack = (0, import_react11.useCallback)(() => setConfirmVisible(false), []);
1631
1728
  return { confirmVisible, requestBack, cancelBack };
1632
1729
  }
1633
1730
 
1634
1731
  // src/useZoneClampedAlign.ts
1635
1732
  var import_split_screen = require("@tastic/split-screen");
1636
- var import_react11 = require("react");
1637
- var import_react_native25 = require("react-native");
1638
- var import_react_native_safe_area_context = require("react-native-safe-area-context");
1733
+ var import_react12 = require("react");
1734
+ var import_react_native27 = require("react-native");
1735
+ var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
1639
1736
  var EDGE_MARGIN2 = 12;
1640
1737
  function useZoneClampedAlign(open, contentWidth, contentHeight, rotation = 0) {
1641
1738
  const auto = useAutoAlign(open, contentWidth, contentHeight, rotation);
1642
1739
  const zone = (0, import_split_screen.useZoneBounds)();
1643
- const { height: windowHeight } = (0, import_react_native25.useWindowDimensions)();
1644
- const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
1740
+ const { height: windowHeight } = (0, import_react_native27.useWindowDimensions)();
1741
+ const insets = (0, import_react_native_safe_area_context2.useSafeAreaInsets)();
1645
1742
  const shouldMeasure = open && auto.measured && zone !== null;
1646
- const [ownBounds, setOwnBounds] = (0, import_react11.useState)(null);
1647
- const [prevShouldMeasure, setPrevShouldMeasure] = (0, import_react11.useState)(shouldMeasure);
1743
+ const [ownBounds, setOwnBounds] = (0, import_react12.useState)(null);
1744
+ const [prevShouldMeasure, setPrevShouldMeasure] = (0, import_react12.useState)(shouldMeasure);
1648
1745
  if (shouldMeasure !== prevShouldMeasure) {
1649
1746
  setPrevShouldMeasure(shouldMeasure);
1650
1747
  setOwnBounds(null);
1651
1748
  }
1652
- (0, import_react11.useEffect)(() => {
1749
+ (0, import_react12.useEffect)(() => {
1653
1750
  if (!shouldMeasure) return;
1654
1751
  auto.triggerRef.current?.measureInWindow((_x, y, _width, height) => {
1655
1752
  const belowShared = zone.zoneSide === "belowShared";
@@ -1673,6 +1770,7 @@ function useZoneClampedAlign(open, contentWidth, contentHeight, rotation = 0) {
1673
1770
  0 && (module.exports = {
1674
1771
  AchievementCatalogSection,
1675
1772
  AchievementRow,
1773
+ AchievementUnlockList,
1676
1774
  ActivityStatSection,
1677
1775
  BaseSettingsDialog,
1678
1776
  BaseStatsScreen,
@@ -1690,6 +1788,7 @@ function useZoneClampedAlign(open, contentWidth, contentHeight, rotation = 0) {
1690
1788
  PopoverBody,
1691
1789
  PressAwayOverlay,
1692
1790
  ReadyButton,
1791
+ RotatedFrame,
1693
1792
  SectionedDropdown,
1694
1793
  SharedActionBand,
1695
1794
  SkiaGate,
@@ -1705,6 +1804,7 @@ function useZoneClampedAlign(open, contentWidth, contentHeight, rotation = 0) {
1705
1804
  getOverlayCardColors,
1706
1805
  getStaggeredWordDuration,
1707
1806
  loadSkiaWeb,
1807
+ overlayActionStyles,
1708
1808
  overlayStyles,
1709
1809
  useAutoAlign,
1710
1810
  usePopoverHost,