@tastic/hud 0.14.2 → 0.15.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.
Files changed (53) hide show
  1. package/README.md +110 -79
  2. package/dist/chrome/index.d.mts +239 -0
  3. package/dist/chrome/index.d.ts +239 -0
  4. package/dist/chrome/index.js +772 -0
  5. package/dist/chrome/index.mjs +44 -0
  6. package/dist/chunk-OTMMST5M.mjs +645 -0
  7. package/dist/guide/index.js +3 -3
  8. package/dist/guide/index.mjs +3 -3
  9. package/dist/index.d.mts +21 -252
  10. package/dist/index.d.ts +21 -252
  11. package/dist/index.js +20 -20
  12. package/dist/index.mjs +186 -791
  13. package/package.json +8 -1
  14. package/src/AchievementCatalogSection.tsx +1 -1
  15. package/src/AchievementRow.tsx +9 -9
  16. package/src/BaseSettingsDialog.tsx +31 -31
  17. package/src/BaseStatsScreen.tsx +13 -13
  18. package/src/ConfirmDialog.tsx +2 -2
  19. package/src/ControlSchemePicker.tsx +9 -9
  20. package/src/CornerActionButtons.tsx +6 -6
  21. package/src/CornerStatusBadges.tsx +8 -8
  22. package/src/InlineColorPicker.tsx +30 -30
  23. package/src/LabeledDropdown.tsx +8 -8
  24. package/src/PlayerSetupPanel.tsx +25 -25
  25. package/src/PopoverBody.tsx +16 -16
  26. package/src/PressAwayOverlay.tsx +2 -2
  27. package/src/ReadyButton.tsx +3 -3
  28. package/src/SectionedDropdown.tsx +31 -31
  29. package/src/SharedActionBand.tsx +6 -6
  30. package/src/SkiaGate.tsx +5 -5
  31. package/src/SkiaGate.web.tsx +2 -2
  32. package/src/StaggeredWord.tsx +9 -9
  33. package/src/StatRow.tsx +3 -3
  34. package/src/StatSection.tsx +3 -3
  35. package/src/TriggerGauge.tsx +54 -54
  36. package/src/TriggerGaugeHost.tsx +2 -2
  37. package/src/chrome/index.ts +22 -0
  38. package/src/fonts.ts +1 -1
  39. package/src/guide/DirectionKeysHint.tsx +3 -3
  40. package/src/guide/GuideProvider.tsx +7 -7
  41. package/src/guide/HowToPlayDialog.tsx +7 -7
  42. package/src/guide/SeatDiagram.tsx +3 -3
  43. package/src/guide/SwipeHint.tsx +4 -4
  44. package/src/guide/guideSlice.ts +7 -7
  45. package/src/guide/index.ts +4 -3
  46. package/src/guide/types.ts +1 -1
  47. package/src/index.ts +1 -1
  48. package/src/loadSkiaWeb.ts +2 -2
  49. package/src/loadSkiaWeb.web.ts +2 -2
  50. package/src/overlayCard.ts +3 -3
  51. package/src/useAutoAlign.ts +23 -23
  52. package/src/usePopoverHost.ts +1 -1
  53. package/src/useZoneClampedAlign.ts +23 -23
package/dist/index.mjs CHANGED
@@ -1,129 +1,52 @@
1
1
  import {
2
2
  SkiaGate
3
3
  } from "./chunk-XEOFR236.mjs";
4
+ import {
5
+ AchievementCatalogSection,
6
+ AchievementRow,
7
+ ActivityStatSection,
8
+ BaseSettingsDialog,
9
+ BaseStatsScreen,
10
+ ConfirmDialog,
11
+ ContentGutter,
12
+ CornerActionButtons,
13
+ LOCKED_BADGE_COLOR,
14
+ MONO_FONT,
15
+ RotatedFrame,
16
+ StaggeredWord,
17
+ StatRow,
18
+ StatSection,
19
+ UpdateDialog,
20
+ getStaggeredWordDuration
21
+ } from "./chunk-OTMMST5M.mjs";
4
22
  import {
5
23
  getOverlayCardColors,
6
24
  overlayActionStyles,
7
25
  overlayStyles
8
26
  } from "./chunk-32U2OIRW.mjs";
9
27
 
10
- // src/AchievementCatalogSection.tsx
11
- import { useAutoPaperTheme as useAutoPaperTheme2 } from "@rific/auto-paper";
12
- import { Fragment } from "react";
13
- import { StyleSheet as StyleSheet2 } from "react-native";
14
- import { Text as Text2 } from "react-native-paper";
15
-
16
- // src/AchievementRow.tsx
17
- import { useAutoPaperTheme } from "@rific/auto-paper";
18
- import { StyleSheet, View } from "react-native";
19
- import { Icon, ProgressBar, Text } from "react-native-paper";
20
- import { jsx, jsxs } from "react/jsx-runtime";
21
- var LOCKED_BADGE_COLOR = "rgba(128,128,128,0.3)";
22
- function AchievementRow({ icon, title, description, badgeColor, unlockedLabel, checkColor, progress, deviceMarker, fg: fgOverride, fgMuted: fgMutedOverride, sectionBg: sectionBgOverride }) {
23
- const { dark } = useAutoPaperTheme();
24
- const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
25
- const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
26
- const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
27
- const unlocked = unlockedLabel !== void 0;
28
- return /* @__PURE__ */ jsxs(View, { style: [styles.row, { backgroundColor: sectionBg }], children: [
29
- /* @__PURE__ */ jsx(View, { style: [styles.badge, { backgroundColor: badgeColor }], children: /* @__PURE__ */ jsx(Icon, { source: icon, size: 20, color: unlocked ? "#000000" : fgMuted }) }),
30
- /* @__PURE__ */ jsxs(View, { style: styles.text, children: [
31
- /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: [styles.boldText, { color: fg }], children: title }),
32
- /* @__PURE__ */ jsx(Text, { variant: "bodySmall", style: { color: fgMuted }, children: description }),
33
- deviceMarker && /* @__PURE__ */ jsxs(View, { style: styles.deviceMarker, children: [
34
- /* @__PURE__ */ jsx(Icon, { source: "earth", size: 11, color: fgMuted }),
35
- /* @__PURE__ */ jsx(Text, { variant: "labelSmall", style: { color: fgMuted }, children: "Same for every profile" })
36
- ] }),
37
- !unlocked && progress !== void 0 && /* @__PURE__ */ jsx(ProgressBar, { progress, color: checkColor ?? badgeColor, style: styles.progressBar })
38
- ] }),
39
- unlocked ? /* @__PURE__ */ jsxs(View, { style: styles.status, children: [
40
- /* @__PURE__ */ jsx(Icon, { source: "check-circle", size: 20, color: checkColor ?? badgeColor }),
41
- /* @__PURE__ */ jsx(Text, { variant: "labelSmall", style: { color: fgMuted }, children: unlockedLabel })
42
- ] }) : /* @__PURE__ */ jsx(Icon, { source: "lock-outline", size: 20, color: fgMuted })
43
- ] });
44
- }
45
- var styles = StyleSheet.create({
46
- badge: {
47
- alignItems: "center",
48
- borderRadius: 10,
49
- height: 36,
50
- justifyContent: "center",
51
- width: 36
52
- },
53
- boldText: {
54
- fontWeight: "bold"
55
- },
56
- deviceMarker: {
57
- alignItems: "center",
58
- flexDirection: "row",
59
- gap: 4
60
- },
61
- progressBar: {
62
- borderRadius: 4,
63
- height: 6,
64
- marginTop: 6
65
- },
66
- row: {
67
- alignItems: "center",
68
- borderRadius: 12,
69
- flexDirection: "row",
70
- gap: 12,
71
- padding: 12
72
- },
73
- status: {
74
- alignItems: "center",
75
- gap: 2,
76
- width: 64
77
- },
78
- text: {
79
- flex: 1,
80
- gap: 2
81
- }
82
- });
83
-
84
- // src/fonts.ts
85
- import { Platform } from "react-native";
86
- var MONO_FONT = Platform.select({ ios: "Menlo", android: "monospace", default: "monospace" });
87
-
88
- // src/AchievementCatalogSection.tsx
89
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
90
- function AchievementCatalogSection({ rows, label = "ALL ACHIEVEMENTS", fg, fgMuted: fgMutedOverride }) {
91
- const { dark } = useAutoPaperTheme2();
92
- const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
93
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
94
- /* @__PURE__ */ jsx2(Text2, { variant: "labelMedium", style: [styles2.label, { color: fgMuted, fontFamily: MONO_FONT }], children: label }),
95
- rows.map((row) => /* @__PURE__ */ jsx2(AchievementRow, { icon: row.icon, title: row.title, description: row.description, badgeColor: row.unlockedAt !== void 0 ? row.tierColor : LOCKED_BADGE_COLOR, checkColor: row.tierColor, unlockedLabel: row.unlockedLabel, progress: row.progress, deviceMarker: row.deviceMarker, fg, fgMuted: fgMutedOverride }, row.id))
96
- ] });
97
- }
98
- var styles2 = StyleSheet2.create({
99
- label: {
100
- letterSpacing: 2,
101
- marginTop: 8
102
- }
103
- });
104
-
105
28
  // src/AchievementUnlockList.tsx
106
- import { getContrastColor, useAutoPaperTheme as useAutoPaperTheme3 } from "@rific/auto-paper";
29
+ import { getContrastColor, useAutoPaperTheme } from "@rific/auto-paper";
107
30
  import { ProfileChip } from "@tastic/profile";
108
- import { StyleSheet as StyleSheet3, View as View2 } from "react-native";
109
- import { Icon as Icon2, Text as Text3 } from "react-native-paper";
110
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
31
+ import { StyleSheet, View } from "react-native";
32
+ import { Icon, Text } from "react-native-paper";
33
+ import { jsx, jsxs } from "react/jsx-runtime";
111
34
  var AVATAR_SIZE = 18;
112
35
  function AchievementUnlockList({ unlocks, seats, owners, tierColors, fg: fgOverride, style }) {
113
- const { dark } = useAutoPaperTheme3();
36
+ const { dark } = useAutoPaperTheme();
114
37
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
115
38
  const rows = seats.flatMap((seat) => (unlocks[seat] ?? []).map((achievement) => ({ seat, achievement })));
116
39
  if (rows.length === 0) return null;
117
- return /* @__PURE__ */ jsx3(View2, { style: [styles3.list, style], children: rows.map(({ seat, achievement }) => {
40
+ return /* @__PURE__ */ jsx(View, { style: [styles.list, style], children: rows.map(({ seat, achievement }) => {
118
41
  const owner = owners[seat];
119
- return /* @__PURE__ */ jsxs3(View2, { style: styles3.row, children: [
120
- owner.profile ? /* @__PURE__ */ jsx3(ProfileChip, { profile: owner.profile, size: AVATAR_SIZE }) : /* @__PURE__ */ jsx3(View2, { style: [styles3.circle, { backgroundColor: owner.color }], children: /* @__PURE__ */ jsx3(Icon2, { source: "account", size: 10, color: getContrastColor(owner.color) }) }),
121
- /* @__PURE__ */ jsx3(View2, { style: [styles3.circle, { backgroundColor: tierColors[achievement.tier] }], children: /* @__PURE__ */ jsx3(Icon2, { source: achievement.icon, size: 12, color: "#000000" }) }),
122
- /* @__PURE__ */ jsx3(Text3, { style: [styles3.label, { color: fg }], numberOfLines: 1, children: achievement.title })
42
+ return /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
43
+ owner.profile ? /* @__PURE__ */ jsx(ProfileChip, { profile: owner.profile, size: AVATAR_SIZE }) : /* @__PURE__ */ jsx(View, { style: [styles.circle, { backgroundColor: owner.color }], children: /* @__PURE__ */ jsx(Icon, { source: "account", size: 10, color: getContrastColor(owner.color) }) }),
44
+ /* @__PURE__ */ jsx(View, { style: [styles.circle, { backgroundColor: tierColors[achievement.tier] }], children: /* @__PURE__ */ jsx(Icon, { source: achievement.icon, size: 12, color: "#000000" }) }),
45
+ /* @__PURE__ */ jsx(Text, { style: [styles.label, { color: fg }], numberOfLines: 1, children: achievement.title })
123
46
  ] }, `${seat}-${achievement.id}`);
124
47
  }) });
125
48
  }
126
- var styles3 = StyleSheet3.create({
49
+ var styles = StyleSheet.create({
127
50
  circle: {
128
51
  alignItems: "center",
129
52
  borderRadius: AVATAR_SIZE / 2,
@@ -146,517 +69,49 @@ var styles3 = StyleSheet3.create({
146
69
  }
147
70
  });
148
71
 
149
- // src/StatRow.tsx
150
- import { useAutoPaperTheme as useAutoPaperTheme4 } from "@rific/auto-paper";
151
- import { StyleSheet as StyleSheet4, View as View3 } from "react-native";
152
- import { Text as Text4 } from "react-native-paper";
153
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
154
- function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }) {
155
- const { dark } = useAutoPaperTheme4();
156
- const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
157
- const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
158
- return /* @__PURE__ */ jsxs4(View3, { style: styles4.row, children: [
159
- /* @__PURE__ */ jsx4(Text4, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
160
- /* @__PURE__ */ jsx4(Text4, { variant: "bodyMedium", style: [styles4.value, { color: fg }], children: value })
161
- ] });
162
- }
163
- var styles4 = StyleSheet4.create({
164
- row: {
165
- alignItems: "center",
166
- flexDirection: "row",
167
- justifyContent: "space-between"
168
- },
169
- value: {
170
- fontWeight: "bold"
171
- }
172
- });
173
-
174
- // src/StatSection.tsx
175
- import { useAutoPaperTheme as useAutoPaperTheme5 } from "@rific/auto-paper";
176
- import { StyleSheet as StyleSheet5, View as View4 } from "react-native";
177
- import { Text as Text5 } from "react-native-paper";
178
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
179
- function StatSection({ label, fg: fgOverride, sectionBg: sectionBgOverride, children }) {
180
- const { dark } = useAutoPaperTheme5();
181
- const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
182
- const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
183
- return /* @__PURE__ */ jsxs5(View4, { style: [styles5.section, { backgroundColor: sectionBg }], children: [
184
- /* @__PURE__ */ jsx5(Text5, { variant: "labelMedium", style: [styles5.label, { color: fg }], children: label }),
185
- children
186
- ] });
187
- }
188
- var styles5 = StyleSheet5.create({
189
- // Bold and full-contrast (fg), not the original fontless/fgMuted treatment — that read at the
190
- // same visual weight as StatRow's own row labels below it (both used the identical muted color;
191
- // letterSpacing/variant alone didn't separate them enough to read as "this is the group header"
192
- // at a glance). Matches AchievementRow's own title styling (bold + fg) below it in the same list.
193
- label: {
194
- fontWeight: "bold",
195
- letterSpacing: 2
196
- },
197
- section: {
198
- borderRadius: 12,
199
- gap: 8,
200
- padding: 16
201
- }
202
- });
203
-
204
- // src/ActivityStatSection.tsx
205
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
206
- function ActivityStatSection({ stats }) {
207
- return /* @__PURE__ */ jsxs6(StatSection, { label: "ACTIVITY", children: [
208
- /* @__PURE__ */ jsx6(StatRow, { label: "Days Played", value: String(stats.distinctDaysPlayed) }),
209
- /* @__PURE__ */ jsx6(StatRow, { label: "Day Streak", value: String(stats.currentDayStreak) }),
210
- /* @__PURE__ */ jsx6(StatRow, { label: "Best Day Streak", value: String(stats.bestDayStreak) })
211
- ] });
212
- }
213
-
214
- // src/BaseSettingsDialog.tsx
215
- import { AutoAppearancePicker, Dialog, useAutoPaperTheme as useAutoPaperTheme7 } from "@rific/auto-paper";
216
- import { Button as Button2, SoundContext, TouchableRipple, useHapticSettings, useSoundSettings, useVibration } from "@rific/feedback-press";
217
- import { useUpdater } from "@rific/updater";
218
- import { useIsTouchPrimaryDevice, useRotation as useRotation2 } from "@tastic/core";
219
- import { useContext, useState as useState2 } from "react";
220
- import { Platform as Platform2, ScrollView, StyleSheet as StyleSheet8, View as View7 } from "react-native";
221
- import { Icon as Icon4, Portal as Portal3, Text as Text7 } from "react-native-paper";
222
-
223
- // src/ConfirmDialog.tsx
224
- import { useAutoPaperTheme as useAutoPaperTheme6 } from "@rific/auto-paper";
225
- import { Button } from "@rific/feedback-press";
226
- import { toRotationStyle } from "@tastic/core";
227
- import { StyleSheet as StyleSheet6, View as View5 } from "react-native";
228
- import { Icon as Icon3, Portal, Text as Text6 } from "react-native-paper";
229
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
230
- function ConfirmDialog({ visible, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, rotation = 0, icon = "help-circle-outline", destructive = false }) {
231
- const { dark, colors } = useAutoPaperTheme6();
232
- if (!visible) return null;
233
- const { fg, cardBg, cardBorder } = getOverlayCardColors(dark);
234
- const accent = destructive ? colors.danger : colors.primary;
235
- const onAccent = destructive ? colors.onDanger : colors.onPrimary;
236
- const showCancel = cancelLabel !== void 0 && onCancel !== void 0;
237
- return /* @__PURE__ */ jsx7(Portal, { children: /* @__PURE__ */ jsx7(View5, { style: [overlayStyles.overlay, styles6.overlay], children: /* @__PURE__ */ jsxs7(View5, { style: [overlayStyles.card, styles6.card, { backgroundColor: cardBg, borderColor: cardBorder }, toRotationStyle(rotation)], children: [
238
- /* @__PURE__ */ jsx7(Icon3, { source: icon, size: 64, color: accent }),
239
- /* @__PURE__ */ jsx7(Text6, { variant: "headlineLarge", style: [styles6.title, { color: accent }], children: title }),
240
- /* @__PURE__ */ jsx7(Text6, { variant: "bodyLarge", style: [styles6.body, { color: fg }], children: message }),
241
- /* @__PURE__ */ jsxs7(View5, { style: overlayActionStyles.actions, children: [
242
- showCancel && /* @__PURE__ */ jsx7(Button, { mode: "outlined", onPress: onCancel, style: overlayActionStyles.actionButton, children: cancelLabel }),
243
- /* @__PURE__ */ jsx7(Button, { mode: "contained", onPress: onConfirm, style: showCancel ? overlayActionStyles.actionButton : overlayActionStyles.actionButtonSingle, buttonColor: accent, textColor: onAccent, children: confirmLabel })
244
- ] })
245
- ] }) }) });
246
- }
247
- var styles6 = StyleSheet6.create({
248
- body: {
249
- textAlign: "center"
250
- },
251
- // Additive on top of overlayCard's own shared `card` shape (borderRadius/borderWidth/gap/maxWidth/
252
- // padding) — claims its full allowance instead of just maxWidth alone, which only caps how wide the
253
- // card is allowed to get - without an explicit width, a flex-centered child sizes to its own content
254
- // instead of stretching, so the card (and therefore actions below) can end up narrower than the
255
- // screen actually has room for, needlessly forcing the action row to wrap sooner than it has to.
256
- card: {
257
- width: "100%"
258
- },
259
- // Additive on top of overlayCard's own shared `overlay` backdrop — gives the card room to breathe
260
- // from the screen edges now that it reliably stretches to fill its full width allowance (see
261
- // card's own width comment) - without this, width: '100%' would run the card edge-to-edge on a
262
- // screen narrower than maxWidth's own 360.
263
- overlay: {
264
- paddingHorizontal: 24
265
- },
266
- // textAlign is load-bearing, not decorative, once `title` wraps to more than one line - centered
267
- // within card's own alignItems: 'center' only centers the Text's block as a whole; each wrapped
268
- // line inside that block still defaults to left-aligned without this.
269
- title: {
270
- fontWeight: "bold",
271
- textAlign: "center"
272
- }
273
- });
274
-
275
- // src/RotatedFrame.tsx
276
- import { FakeLandscapeView, rotateDimensions, rotateInsets, useRotation } from "@tastic/core";
277
- import { useMemo } from "react";
278
- import { StyleSheet as StyleSheet7, useWindowDimensions, View as View6 } from "react-native";
279
- import { Portal as Portal2 } from "react-native-paper";
280
- import { SafeAreaInsetsContext, useSafeAreaInsets } from "react-native-safe-area-context";
281
- import { jsx as jsx8 } from "react/jsx-runtime";
282
- function RotatedFrame({ children, rotation: rotationOverride, rotatedSafeArea = false, portalHost = false, passThrough = false, maxContentWidth }) {
283
- const ambientRotation = useRotation();
284
- const rotation = rotationOverride ?? ambientRotation;
285
- const window = useWindowDimensions();
286
- const { width, height } = rotateDimensions(window.width, window.height, rotation);
287
- const rawInsets = useSafeAreaInsets();
288
- const insets = useMemo(() => rotatedSafeArea ? rotateInsets(rawInsets, rotation) : rawInsets, [rotatedSafeArea, rawInsets, rotation]);
289
- const cappedWidth = maxContentWidth !== void 0 && width > height ? maxContentWidth : void 0;
290
- const column = /* @__PURE__ */ jsx8(View6, { style: [styles7.column, cappedWidth !== void 0 && { maxWidth: cappedWidth }], pointerEvents: passThrough ? "box-none" : void 0, children });
291
- return /* @__PURE__ */ jsx8(FakeLandscapeView, { rotation, passThrough, style: styles7.root, children: /* @__PURE__ */ jsx8(SafeAreaInsetsContext.Provider, { value: insets, children: portalHost ? /* @__PURE__ */ jsx8(Portal2.Host, { children: column }) : column }) });
292
- }
293
- var styles7 = StyleSheet7.create({
294
- // A centred column: full width up to `maxWidth`, so a capped frame sits in the middle of a wider window.
295
- column: {
296
- alignSelf: "center",
297
- flex: 1,
298
- width: "100%"
299
- },
300
- root: {
301
- flex: 1
302
- }
303
- });
304
-
305
- // src/useUpdateConfirmBridge.ts
306
- import { useCallback, useRef, useState } from "react";
307
- function useUpdateConfirmBridge() {
308
- const [manifest, setManifest] = useState(null);
309
- const resolveRef = useRef(null);
310
- const onConfirm = useCallback(
311
- (next) => new Promise((resolve) => {
312
- resolveRef.current = resolve;
313
- setManifest(next);
314
- }),
315
- []
316
- );
317
- const respond = useCallback((confirmed) => {
318
- setManifest(null);
319
- resolveRef.current?.(confirmed);
320
- resolveRef.current = null;
321
- }, []);
322
- return { manifest, onConfirm, respond };
323
- }
324
-
325
- // src/BaseSettingsDialog.tsx
326
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
327
- function SettingIcon({ source, color, containerColor }) {
328
- return /* @__PURE__ */ jsx9(View7, { style: [styles8.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ jsx9(Icon4, { source, size: 18, color }) });
329
- }
330
- function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, onShowHowToPlay, children }) {
331
- const { colors } = useAutoPaperTheme7();
332
- const ambientRotation = useRotation2();
333
- const rotation = rotationOverride ?? ambientRotation;
334
- const isTouchPrimary = useIsTouchPrimaryDevice();
335
- const { settings: hapticSettings, set: setHapticSettings } = useHapticSettings();
336
- const { settings: soundSettings, set: setSoundSettings } = useSoundSettings();
337
- const sound = useContext(SoundContext);
338
- const { forceShort: forceHapticFeedback } = useVibration();
339
- const [infoMessage, setInfoMessage] = useState2(null);
340
- const { manifest: updateManifest, onConfirm: onUpdateConfirm, respond: respondToUpdateConfirm } = useUpdateConfirmBridge();
341
- const { check, checking, updateReady } = useUpdater({
342
- autoCheck: false,
343
- autoPrompt: false,
344
- onConfirm: onUpdateConfirm,
345
- onError: onUpdateError,
346
- onInfo: (title, message) => setInfoMessage({ title, message })
347
- });
348
- const updateManifestDate = updateManifest ? new Date(updateManifest.createdAt) : null;
349
- const [opened, setOpened] = useState2(visible);
350
- if (visible && !opened) setOpened(true);
351
- const showLockOrientation = isTouchPrimary && lockOrientation !== void 0 && onLockOrientationChange !== void 0;
352
- const showEdgeGuard = Platform2.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
353
- const showHaptics = Platform2.OS !== "web" && !hideHaptics;
354
- const showSound = !hideSound;
355
- const showAppearance = !hideAppearance;
356
- const showUpdateCheck = Platform2.OS !== "web" && !hideUpdateCheck;
357
- if (!opened) return null;
358
- return /* @__PURE__ */ jsx9(Portal3, { children: /* @__PURE__ */ jsxs8(RotatedFrame, { rotation, rotatedSafeArea: true, portalHost: true, passThrough: true, children: [
359
- /* @__PURE__ */ jsxs8(Dialog, { visible, onDismiss, style: styles8.dialog, children: [
360
- /* @__PURE__ */ jsx9(Dialog.Title, { children: "Settings" }),
361
- /* @__PURE__ */ jsx9(Dialog.ScrollArea, { style: styles8.scrollArea, children: /* @__PURE__ */ jsxs8(ScrollView, { contentContainerStyle: styles8.content, showsVerticalScrollIndicator: false, children: [
362
- /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleGroup, children: [
363
- showLockOrientation && /* @__PURE__ */ jsx9(TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles8.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleContent, children: [
364
- /* @__PURE__ */ jsx9(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.tertiary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.tertiaryContainer : colors.surfaceVariant }),
365
- /* @__PURE__ */ jsxs8(View7, { style: styles8.flexShrink, children: [
366
- /* @__PURE__ */ jsx9(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
367
- /* @__PURE__ */ jsx9(Text7, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
368
- ] })
369
- ] }) }),
370
- showEdgeGuard && /* @__PURE__ */ jsx9(TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles8.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleContent, children: [
371
- /* @__PURE__ */ jsx9(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.tertiary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.tertiaryContainer : colors.surfaceVariant }),
372
- /* @__PURE__ */ jsxs8(View7, { style: styles8.flexShrink, children: [
373
- /* @__PURE__ */ jsx9(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
374
- /* @__PURE__ */ jsx9(Text7, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
375
- ] })
376
- ] }) }),
377
- (showSound || showHaptics) && /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleRow, children: [
378
- showSound && /* @__PURE__ */ jsx9(
379
- TouchableRipple,
380
- {
381
- soundDisabled: true,
382
- onPress: () => {
383
- const enabled = !soundSettings.enabled;
384
- setSoundSettings({ enabled });
385
- if (enabled) sound.selection?.();
386
- },
387
- style: styles8.toggleButtonInRow,
388
- accessibilityLabel: `Sound ${soundSettings.enabled ? "on" : "off"}`,
389
- children: /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleContent, children: [
390
- /* @__PURE__ */ jsx9(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
391
- /* @__PURE__ */ jsx9(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
392
- ] })
393
- }
394
- ),
395
- showHaptics && /* @__PURE__ */ jsx9(
396
- TouchableRipple,
397
- {
398
- hapticDisabled: true,
399
- onPress: () => {
400
- const vibrate = !hapticSettings.vibrate;
401
- setHapticSettings({ vibrate });
402
- if (vibrate) forceHapticFeedback();
403
- },
404
- style: styles8.toggleButtonInRow,
405
- accessibilityLabel: `Haptics ${hapticSettings.vibrate ? "on" : "off"}`,
406
- children: /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleContent, children: [
407
- /* @__PURE__ */ jsx9(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
408
- /* @__PURE__ */ jsx9(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
409
- ] })
410
- }
411
- )
412
- ] }),
413
- onShowHowToPlay && /* @__PURE__ */ jsx9(
414
- TouchableRipple,
415
- {
416
- onPress: () => {
417
- onDismiss();
418
- onShowHowToPlay();
419
- },
420
- style: styles8.toggleButton,
421
- accessibilityLabel: "How to Play",
422
- children: /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleContent, children: [
423
- /* @__PURE__ */ jsx9(SettingIcon, { source: "help-circle-outline", color: colors.secondary, containerColor: colors.secondaryContainer }),
424
- /* @__PURE__ */ jsxs8(View7, { style: styles8.flexShrink, children: [
425
- /* @__PURE__ */ jsx9(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "How to Play" }),
426
- /* @__PURE__ */ jsx9(Text7, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Replay the quick intro" })
427
- ] })
428
- ] })
429
- }
430
- )
431
- ] }),
432
- showAppearance && /* @__PURE__ */ jsxs8(View7, { style: styles8.section, children: [
433
- /* @__PURE__ */ jsx9(Text7, { variant: "labelMedium", style: [styles8.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
434
- /* @__PURE__ */ jsx9(AutoAppearancePicker, { showLabels: false })
435
- ] }),
436
- children,
437
- showUpdateCheck && /* @__PURE__ */ jsxs8(View7, { style: styles8.section, children: [
438
- /* @__PURE__ */ jsxs8(Text7, { variant: "labelSmall", style: [styles8.sectionLabel, { color: colors.onSurfaceVariant }], children: [
439
- "VERSION ",
440
- version,
441
- updateReady ? " \xB7 UPDATE READY" : ""
442
- ] }),
443
- /* @__PURE__ */ jsx9(Button2, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
444
- ] })
445
- ] }) })
446
- ] }),
447
- /* @__PURE__ */ jsx9(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) }),
448
- /* @__PURE__ */ jsx9(ConfirmDialog, { visible: !!infoMessage, title: infoMessage?.title ?? "", message: infoMessage?.message ?? "", confirmLabel: "OK", icon: "information-outline", rotation: 0, onConfirm: () => setInfoMessage(null) })
449
- ] }) });
450
- }
451
- var styles8 = StyleSheet8.create({
452
- content: {
453
- gap: 24,
454
- paddingVertical: 20
455
- },
456
- // Caps the card so it never grows past the screen — without this the dialog just keeps
457
- // growing to fit its content and the overflow gets clipped by the screen edge, which is
458
- // what happened in landscape where there's less height to work with. Dialog.ScrollArea +
459
- // ScrollView below then take over and let the content scroll within that bound.
460
- dialog: {
461
- maxHeight: "90%"
462
- },
463
- flexShrink: {
464
- flexShrink: 1
465
- },
466
- iconBadge: {
467
- alignItems: "center",
468
- borderRadius: 10,
469
- height: 36,
470
- justifyContent: "center",
471
- width: 36
472
- },
473
- scrollArea: {
474
- marginBottom: 0
475
- },
476
- section: {
477
- gap: 12
478
- },
479
- sectionLabel: {
480
- letterSpacing: 2
481
- },
482
- // Used to carry marginHorizontal: -12 + paddingHorizontal: 12 (netting to the same inset as
483
- // APPEARANCE/SOUND & HAPTICS below, while giving the ripple/hover highlight room to breathe
484
- // into the dialog's own gutter beyond that). Removed: this dialog scrolls (Dialog.ScrollArea +
485
- // ScrollView above), and on web a ScrollView's own content wrapper clips to its own bounds
486
- // regardless of a child's negative margin - confirmed live, the icon's own rounded-square badge
487
- // was getting its left edge sliced off by exactly that overhang. No horizontal inset of its own
488
- // now - it relies purely on the same dialog padding APPEARANCE/every other row already does,
489
- // which can never overflow because it's never negative.
490
- toggleButton: {
491
- borderRadius: 12,
492
- overflow: "hidden",
493
- paddingVertical: 10
494
- },
495
- // Same shape as toggleButton, `flex: 1` instead of its own width - two of these side by side in
496
- // toggleRow share the row's own edge alignment instead of each carrying their own.
497
- toggleButtonInRow: {
498
- borderRadius: 12,
499
- flex: 1,
500
- overflow: "hidden",
501
- paddingVertical: 10
502
- },
503
- toggleContent: {
504
- alignItems: "center",
505
- flexDirection: "row",
506
- gap: 12
507
- },
508
- toggleGroup: {
509
- gap: 4
510
- },
511
- // No horizontal margin of its own now either - see toggleButton's identical note.
512
- toggleRow: {
513
- flexDirection: "row",
514
- gap: 4
515
- }
516
- });
517
-
518
- // src/BaseStatsScreen.tsx
519
- import { useAutoPaperTheme as useAutoPaperTheme8 } from "@rific/auto-paper";
520
- import { Button as Button3, IconButton } from "@rific/feedback-press";
521
- import { useRotation as useRotation3 } from "@tastic/core";
522
- import { useState as useState3 } from "react";
523
- import { ScrollView as ScrollView2, StyleSheet as StyleSheet9, View as View8 } from "react-native";
524
- import { Text as Text8 } from "react-native-paper";
525
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
526
- 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 }) {
527
- const { dark, colors } = useAutoPaperTheme8();
528
- const ambientRotation = useRotation3();
529
- const rotation = rotationOverride ?? ambientRotation;
530
- const [confirmResetVisible, setConfirmResetVisible] = useState3(false);
531
- const showReset = !!onReset;
532
- const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
533
- const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
534
- return /* @__PURE__ */ jsxs9(View8, { style: [styles9.container, { backgroundColor: bg }], children: [
535
- /* @__PURE__ */ jsxs9(View8, { style: [styles9.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
536
- /* @__PURE__ */ jsx10(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
537
- /* @__PURE__ */ jsx10(Text8, { variant: titleVariant, style: [styles9.title, { color: fg }], children: title })
538
- ] }),
539
- /* @__PURE__ */ jsxs9(ScrollView2, { style: styles9.scrollView, contentContainerStyle: [styles9.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
540
- children,
541
- showReset && /* @__PURE__ */ jsx10(Button3, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles9.resetButton, children: resetLabel })
542
- ] }),
543
- /* @__PURE__ */ jsx10(
544
- ConfirmDialog,
545
- {
546
- visible: confirmResetVisible,
547
- title: resetConfirmTitle,
548
- message: resetConfirmBody,
549
- confirmLabel: "Reset",
550
- cancelLabel: "Cancel",
551
- icon: "alert-outline",
552
- destructive: true,
553
- rotation,
554
- onCancel: () => setConfirmResetVisible(false),
555
- onConfirm: () => {
556
- onReset?.();
557
- setConfirmResetVisible(false);
558
- }
559
- }
560
- )
561
- ] });
562
- }
563
- var styles9 = StyleSheet9.create({
564
- container: {
565
- flex: 1
566
- },
567
- content: {
568
- gap: 12,
569
- paddingHorizontal: 20
570
- },
571
- header: {
572
- alignItems: "center",
573
- flexDirection: "row",
574
- gap: 4,
575
- paddingBottom: 8
576
- },
577
- resetButton: {
578
- marginTop: 16
579
- },
580
- // Bounds the ScrollView to the space `container`'s flex:1 actually gives it — without this, a
581
- // ScrollView with only a contentContainerStyle isn't reliably height-constrained on native (it
582
- // can render at its full unclipped content height instead of the screen's), which leaves the
583
- // header's back button touch target unreliable underneath it.
584
- scrollView: {
585
- flex: 1
586
- },
587
- title: {
588
- flexShrink: 1,
589
- fontWeight: "bold"
590
- }
591
- });
592
-
593
- // src/ContentGutter.tsx
594
- import { computeContentBounds, useRotatedWindowDimensions } from "@tastic/core";
595
- import { StyleSheet as StyleSheet10, View as View9 } from "react-native";
596
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
597
- function ContentGutter({ maxContentWidth, leftGutter, rightGutter, style, children }) {
598
- const { width: windowWidth } = useRotatedWindowDimensions();
599
- const { contentWidth, gutterWidth } = computeContentBounds(windowWidth, maxContentWidth ?? Infinity);
600
- return /* @__PURE__ */ jsxs10(View9, { style: [styles10.row, style], children: [
601
- /* @__PURE__ */ jsx11(View9, { style: [styles10.gutter, { width: gutterWidth }], children: leftGutter }),
602
- /* @__PURE__ */ jsx11(View9, { style: { width: contentWidth }, children }),
603
- /* @__PURE__ */ jsx11(View9, { style: [styles10.gutter, { width: gutterWidth }], children: rightGutter })
604
- ] });
605
- }
606
- var styles10 = StyleSheet10.create({
607
- // overflow:'hidden' keeps an oversized leftGutter/rightGutter from bleeding into the content
608
- // column instead of clipping cleanly at its own real width.
609
- gutter: {
610
- overflow: "hidden"
611
- },
612
- row: {
613
- flexDirection: "row"
614
- }
615
- });
616
-
617
72
  // src/ControlSchemePicker.tsx
618
- import { useIsTouchPrimaryDevice as useIsTouchPrimaryDevice2 } from "@tastic/core";
619
- import { Platform as Platform3 } from "react-native";
73
+ import { useIsTouchPrimaryDevice } from "@tastic/core";
74
+ import { Platform } from "react-native";
620
75
 
621
76
  // src/SectionedDropdown.tsx
622
77
  import { getBlendedColor, getColorRoles } from "@rific/auto-paper";
623
- import { IconButton as IconButton2, TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
624
- import { useRotation as useRotation4 } from "@tastic/core";
625
- import { ScrollView as ScrollView3, StyleSheet as StyleSheet12, View as View12 } from "react-native";
626
- import { Icon as Icon5, Text as Text9 } from "react-native-paper";
78
+ import { IconButton, TouchableRipple } from "@rific/feedback-press";
79
+ import { useRotation } from "@tastic/core";
80
+ import { ScrollView, StyleSheet as StyleSheet3, View as View4 } from "react-native";
81
+ import { Icon as Icon2, Text as Text2 } from "react-native-paper";
627
82
 
628
83
  // src/PopoverBody.tsx
629
- import { StyleSheet as StyleSheet11, View as View10 } from "react-native";
630
- import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
84
+ import { StyleSheet as StyleSheet2, View as View2 } from "react-native";
85
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
631
86
  var CARET_SIZE = 7;
632
87
  var CARET_RING_OFFSET = 2;
633
88
  var CARET_DIP = 1;
634
89
  function PopoverBody({ visible, children, align = "center", verticalAlign = "below", caretColor, caretBorderColor, caretBorderWidth = 1, triggerSize = 0 }) {
635
90
  if (!visible) return null;
636
91
  const above = verticalAlign === "above";
637
- const alignStyle = align === "left" ? styles11.contentLeft : align === "right" ? styles11.contentRight : styles11.contentCenter;
638
- const verticalStyle = above ? styles11.contentAbove : styles11.contentBelow;
92
+ const alignStyle = align === "left" ? styles2.contentLeft : align === "right" ? styles2.contentRight : styles2.contentCenter;
93
+ const verticalStyle = above ? styles2.contentAbove : styles2.contentBelow;
639
94
  const ringSize = CARET_SIZE + caretBorderWidth;
640
95
  const caretOffset = (halfFootprint) => align === "right" ? { right: triggerSize / 2 - halfFootprint } : { left: triggerSize / 2 - halfFootprint };
641
96
  const caretFill = above ? "borderTopColor" : "borderBottomColor";
642
97
  const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
643
98
  const caretEdge = (size) => above ? { bottom: -size + CARET_DIP } : { top: -size + CARET_DIP };
644
- return /* @__PURE__ */ jsxs11(View10, { style: [styles11.content, alignStyle, verticalStyle], children: [
99
+ return /* @__PURE__ */ jsxs2(View2, { style: [styles2.content, alignStyle, verticalStyle], children: [
645
100
  children,
646
- caretColor && /* @__PURE__ */ jsxs11(Fragment2, { children: [
647
- /* @__PURE__ */ jsx12(View10, { style: [styles11.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
648
- /* @__PURE__ */ jsx12(View10, { style: [styles11.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
101
+ caretColor && /* @__PURE__ */ jsxs2(Fragment, { children: [
102
+ /* @__PURE__ */ jsx2(View2, { style: [styles2.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
103
+ /* @__PURE__ */ jsx2(View2, { style: [styles2.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
649
104
  ] })
650
105
  ] });
651
106
  }
652
- var styles11 = StyleSheet11.create({
107
+ var styles2 = StyleSheet2.create({
653
108
  caret: {
654
109
  borderLeftColor: "transparent",
655
110
  borderRightColor: "transparent",
656
111
  height: 0,
657
112
  position: "absolute",
658
113
  width: 0,
659
- // Explicit, not left to DOM order — the box it dips into is itself a position:'relative' view,
114
+ // Explicit, not left to DOM order - the box it dips into is itself a position:'relative' view,
660
115
  // which React Native Web always gives an implicit zIndex:0 (see PopoverBody's stacking-context
661
116
  // notes elsewhere in this codebase), so painting on top needs a real zIndex to beat that
662
117
  // reliably rather than relying on being the later sibling.
@@ -675,7 +130,7 @@ var styles11 = StyleSheet11.create({
675
130
  top: "100%"
676
131
  },
677
132
  // Stretched to the anchor's own (trigger) width, with alignItems centering the actual (usually
678
- // much wider) popover box inside via plain flexbox — not `left:'50%'` plus a percentage
133
+ // much wider) popover box inside via plain flexbox - not `left:'50%'` plus a percentage
679
134
  // `transform`, which depends on native resolving the box's own auto width before applying the
680
135
  // transform and has proven unreliable there, visibly shifting the box off-center.
681
136
  contentCenter: {
@@ -692,15 +147,15 @@ var styles11 = StyleSheet11.create({
692
147
  });
693
148
 
694
149
  // src/TriggerGaugeHost.tsx
695
- import { jsx as jsx13 } from "react/jsx-runtime";
150
+ import { jsx as jsx3 } from "react/jsx-runtime";
696
151
  var getTriggerGauge = () => import("./TriggerGauge-2OJOOF6I.mjs").then((mod) => ({ default: mod.TriggerGauge }));
697
152
  function TriggerGaugeHost(props) {
698
- return /* @__PURE__ */ jsx13(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
153
+ return /* @__PURE__ */ jsx3(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
699
154
  }
700
155
 
701
156
  // src/useAutoAlign.ts
702
- import { useCallback as useCallback2, useEffect, useRef as useRef2, useState as useState4 } from "react";
703
- import { useWindowDimensions as useWindowDimensions2 } from "react-native";
157
+ import { useCallback, useEffect, useRef, useState } from "react";
158
+ import { useWindowDimensions } from "react-native";
704
159
  var EDGE_MARGIN = 12;
705
160
  function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
706
161
  if (rotation === 0) return { x, y, width, height };
@@ -712,18 +167,18 @@ function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
712
167
  return { x: cx + newDx - newWidth / 2, y: cy + newDy - newHeight / 2, width: newWidth, height: newHeight };
713
168
  }
714
169
  function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
715
- const triggerRef = useRef2(null);
716
- const { width: windowWidth, height: windowHeight } = useWindowDimensions2();
717
- const [align, setAlign] = useState4("center");
718
- const [verticalAlign, setVerticalAlign] = useState4("below");
719
- const [maxHeight, setMaxHeight] = useState4(contentHeight);
720
- const [measured, setMeasured] = useState4(false);
721
- const [prevOpen, setPrevOpen] = useState4(open);
170
+ const triggerRef = useRef(null);
171
+ const { width: windowWidth, height: windowHeight } = useWindowDimensions();
172
+ const [align, setAlign] = useState("center");
173
+ const [verticalAlign, setVerticalAlign] = useState("below");
174
+ const [maxHeight, setMaxHeight] = useState(contentHeight);
175
+ const [measured, setMeasured] = useState(false);
176
+ const [prevOpen, setPrevOpen] = useState(open);
722
177
  if (open !== prevOpen) {
723
178
  setPrevOpen(open);
724
179
  if (open) setMeasured(false);
725
180
  }
726
- const measure = useCallback2(() => {
181
+ const measure = useCallback(() => {
727
182
  triggerRef.current?.measureInWindow((rawX, rawY, rawWidth, rawHeight) => {
728
183
  const { x, y, width: triggerWidth, height: triggerHeight } = rotateRect(rawX, rawY, rawWidth, rawHeight, rotation, windowWidth, windowHeight);
729
184
  const centerX = x + triggerWidth / 2;
@@ -747,7 +202,7 @@ function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
747
202
  }
748
203
 
749
204
  // src/SectionedDropdown.tsx
750
- import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
205
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
751
206
  var MENU_BORDER_WIDTH = 1;
752
207
  var TRIGGER_SIZE = 44;
753
208
  var MENU_MIN_WIDTH = 200;
@@ -759,7 +214,7 @@ var MENU_ITEM_HEIGHT_WITH_DESCRIPTION = 58;
759
214
  var MENU_DIVIDER_HEIGHT = 9;
760
215
  var MENU_VERTICAL_PADDING = 12;
761
216
  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" } }) {
762
- const ambientRotation = useRotation4();
217
+ const ambientRotation = useRotation();
763
218
  const rotation = rotationOverride ?? ambientRotation;
764
219
  const menuBg = dark ? "#000000" : "#FFFFFF";
765
220
  const menuBorderColor = getBlendedColor(mutedColor, menuBg, 0.5);
@@ -797,17 +252,17 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
797
252
  }
798
253
  gaugeOffset += section.options.length;
799
254
  }
800
- const anchorStyle = [styles12.anchor, open && styles12.anchorOpen];
255
+ const anchorStyle = [styles3.anchor, open && styles3.anchorOpen];
801
256
  const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
802
257
  const dividerStyle = { backgroundColor: menuBorderColor };
803
- return /* @__PURE__ */ jsxs12(View12, { style: anchorStyle, children: [
804
- /* @__PURE__ */ jsxs12(View12, { ref: triggerRef, collapsable: false, style: styles12.triggerBox, children: [
805
- /* @__PURE__ */ jsx14(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
806
- /* @__PURE__ */ jsx14(IconButton2, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
258
+ return /* @__PURE__ */ jsxs3(View4, { style: anchorStyle, children: [
259
+ /* @__PURE__ */ jsxs3(View4, { ref: triggerRef, collapsable: false, style: styles3.triggerBox, children: [
260
+ /* @__PURE__ */ jsx4(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
261
+ /* @__PURE__ */ jsx4(IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
807
262
  ] }),
808
- /* @__PURE__ */ jsx14(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx14(ScrollView3, { style: [styles12.menu, menuStyle], contentContainerStyle: styles12.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs12(View12, { children: [
809
- sectionIndex > 0 && /* @__PURE__ */ jsx14(View12, { style: [styles12.divider, dividerStyle] }),
810
- section.kind === "single" ? /* @__PURE__ */ jsx14(
263
+ /* @__PURE__ */ jsx4(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx4(ScrollView, { style: [styles3.menu, menuStyle], contentContainerStyle: styles3.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs3(View4, { children: [
264
+ sectionIndex > 0 && /* @__PURE__ */ jsx4(View4, { style: [styles3.divider, dividerStyle] }),
265
+ section.kind === "single" ? /* @__PURE__ */ jsx4(
811
266
  SingleSection,
812
267
  {
813
268
  section,
@@ -820,7 +275,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
820
275
  if (autoDismiss) host.close();
821
276
  }
822
277
  }
823
- ) : /* @__PURE__ */ jsx14(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
278
+ ) : /* @__PURE__ */ jsx4(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
824
279
  ] }, section.id)) }) })
825
280
  ] });
826
281
  }
@@ -832,18 +287,18 @@ function SingleSection({
832
287
  labelFontFamily,
833
288
  onSelect
834
289
  }) {
835
- return /* @__PURE__ */ jsx14(Fragment3, { children: section.options.map((option) => {
290
+ return /* @__PURE__ */ jsx4(Fragment2, { children: section.options.map((option) => {
836
291
  const selected = option.value === section.value;
837
292
  const taken = !selected && option.value === section.takenValue;
838
293
  const rowColor = selected ? onAccentColor : mutedColor;
839
- const itemStyle = [styles12.item, selected && { backgroundColor: accentColor }, taken && styles12.itemTaken];
294
+ const itemStyle = [styles3.item, selected && { backgroundColor: accentColor }, taken && styles3.itemTaken];
840
295
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
841
296
  const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
842
- return /* @__PURE__ */ jsx14(TouchableRipple2, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs12(View12, { style: styles12.itemRow, children: [
843
- option.icon && /* @__PURE__ */ jsx14(Icon5, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
844
- /* @__PURE__ */ jsxs12(View12, { style: styles12.itemLabelColumn, children: [
845
- /* @__PURE__ */ jsx14(Text9, { variant: "labelLarge", style: labelStyle, children: option.label }),
846
- option.description && /* @__PURE__ */ jsx14(Text9, { variant: "labelSmall", style: descriptionStyle, children: option.description })
297
+ return /* @__PURE__ */ jsx4(TouchableRipple, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs3(View4, { style: styles3.itemRow, children: [
298
+ option.icon && /* @__PURE__ */ jsx4(Icon2, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
299
+ /* @__PURE__ */ jsxs3(View4, { style: styles3.itemLabelColumn, children: [
300
+ /* @__PURE__ */ jsx4(Text2, { variant: "labelLarge", style: labelStyle, children: option.label }),
301
+ option.description && /* @__PURE__ */ jsx4(Text2, { variant: "labelSmall", style: descriptionStyle, children: option.description })
847
302
  ] })
848
303
  ] }) }, option.value);
849
304
  }) });
@@ -857,23 +312,23 @@ function MultiSection({
857
312
  allClearLabels
858
313
  }) {
859
314
  const allSelected = section.value.length === section.options.length;
860
- return /* @__PURE__ */ jsxs12(Fragment3, { children: [
315
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
861
316
  section.options.map((option, i) => {
862
317
  const selected = section.value.includes(option.value);
863
318
  const rowColor = selected ? onAccentColor : mutedColor;
864
319
  const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
865
320
  const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
866
- const itemStyle = [styles12.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
321
+ const itemStyle = [styles3.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
867
322
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
868
- return /* @__PURE__ */ jsx14(TouchableRipple2, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs12(View12, { style: styles12.itemRow, children: [
869
- option.icon && /* @__PURE__ */ jsx14(Icon5, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
870
- /* @__PURE__ */ jsx14(Text9, { variant: "labelLarge", style: [styles12.itemLabelFlex, labelStyle], children: option.label })
323
+ return /* @__PURE__ */ jsx4(TouchableRipple, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs3(View4, { style: styles3.itemRow, children: [
324
+ option.icon && /* @__PURE__ */ jsx4(Icon2, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
325
+ /* @__PURE__ */ jsx4(Text2, { variant: "labelLarge", style: [styles3.itemLabelFlex, labelStyle], children: option.label })
871
326
  ] }) }, option.value);
872
327
  }),
873
- section.allClear && /* @__PURE__ */ jsx14(TouchableRipple2, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles12.item, children: /* @__PURE__ */ jsx14(Text9, { variant: "labelLarge", style: [styles12.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
328
+ section.allClear && /* @__PURE__ */ jsx4(TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles3.item, children: /* @__PURE__ */ jsx4(Text2, { variant: "labelLarge", style: [styles3.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
874
329
  ] });
875
330
  }
876
- var styles12 = StyleSheet12.create({
331
+ var styles3 = StyleSheet3.create({
877
332
  allClearLabel: {
878
333
  fontWeight: "bold",
879
334
  textAlign: "center"
@@ -882,7 +337,7 @@ var styles12 = StyleSheet12.create({
882
337
  position: "relative"
883
338
  },
884
339
  // React Native Web gives every position:'relative' view an explicit zIndex (0, not 'auto'),
885
- // which makes each one its own stacking context — so a zIndex set only on the popover content
340
+ // which makes each one its own stacking context - so a zIndex set only on the popover content
886
341
  // below only wins comparisons within this anchor's own box, not against this anchor's own later
887
342
  // siblings (e.g. a Ready button), which are otherwise painted on top by DOM-order tiebreak.
888
343
  // Elevating the anchor itself, only while its popover is open, fixes that at the right level.
@@ -890,7 +345,7 @@ var styles12 = StyleSheet12.create({
890
345
  zIndex: 100
891
346
  },
892
347
  divider: {
893
- height: StyleSheet12.hairlineWidth,
348
+ height: StyleSheet3.hairlineWidth,
894
349
  marginVertical: 4
895
350
  },
896
351
  item: {
@@ -909,7 +364,7 @@ var styles12 = StyleSheet12.create({
909
364
  flexDirection: "row",
910
365
  gap: 8
911
366
  },
912
- // Same opacity as ProfilePicker's rowDisabled / InlineColorPicker's swatchTaken — the fleet's
367
+ // Same opacity as ProfilePicker's rowDisabled / InlineColorPicker's swatchTaken - the fleet's
913
368
  // established "shown, not removed" treatment for another seat's own current pick.
914
369
  itemTaken: {
915
370
  opacity: 0.35
@@ -925,7 +380,7 @@ var styles12 = StyleSheet12.create({
925
380
  shadowOpacity: 0.3,
926
381
  shadowRadius: 8
927
382
  },
928
- // Separate from `menu` itself — a ScrollView's own style sets the scrolling frame's bounds
383
+ // Separate from `menu` itself - a ScrollView's own style sets the scrolling frame's bounds
929
384
  // (including the maxHeight that actually caps it), while padding/gap belong to its
930
385
  // contentContainerStyle, which sizes to the *unclamped* content instead of the frame.
931
386
  menuContent: {
@@ -943,49 +398,30 @@ var styles12 = StyleSheet12.create({
943
398
  });
944
399
 
945
400
  // src/ControlSchemePicker.tsx
946
- import { jsx as jsx15 } from "react/jsx-runtime";
401
+ import { jsx as jsx5 } from "react/jsx-runtime";
947
402
  function ControlSchemePicker({ id, host, value, onChange, options, takenValue, accentColor, mutedColor, onAccentColor, dark, align, alignOverride, rotation, labelFontFamily }) {
948
- const isTouchPrimary = useIsTouchPrimaryDevice2();
949
- if (Platform3.OS !== "web" || isTouchPrimary) return null;
950
- return /* @__PURE__ */ jsx15(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 });
951
- }
952
-
953
- // src/CornerActionButtons.tsx
954
- import { IconButton as IconButton3 } from "@rific/feedback-press";
955
- import { StyleSheet as StyleSheet13 } from "react-native";
956
- import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
957
- function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon = "arrow-left", backLabel = "Back" }) {
958
- return /* @__PURE__ */ jsxs13(Fragment4, { children: [
959
- /* @__PURE__ */ jsx16(IconButton3, { icon: backIcon, iconColor: fg, size: 24, style: [styles13.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: backLabel }),
960
- /* @__PURE__ */ jsx16(IconButton3, { icon: "cog", iconColor: fg, size: 24, style: [styles13.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
961
- ] });
403
+ const isTouchPrimary = useIsTouchPrimaryDevice();
404
+ if (Platform.OS !== "web" || isTouchPrimary) return null;
405
+ return /* @__PURE__ */ jsx5(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 });
962
406
  }
963
- var styles13 = StyleSheet13.create({
964
- back: {
965
- position: "absolute"
966
- },
967
- topRight: {
968
- position: "absolute"
969
- }
970
- });
971
407
 
972
408
  // src/CornerStatusBadges.tsx
973
- import { StyleSheet as StyleSheet14, View as View13 } from "react-native";
974
- import { Icon as Icon6 } from "react-native-paper";
975
- import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
409
+ import { StyleSheet as StyleSheet4, View as View5 } from "react-native";
410
+ import { Icon as Icon3 } from "react-native-paper";
411
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
976
412
  var BADGE_SIZE = 30;
977
413
  function StatusBadge({ icon }) {
978
- return /* @__PURE__ */ jsx17(View13, { style: styles14.badge, children: icon && /* @__PURE__ */ jsx17(Icon6, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
414
+ return /* @__PURE__ */ jsx6(View5, { style: styles4.badge, children: icon && /* @__PURE__ */ jsx6(Icon3, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
979
415
  }
980
416
  function CornerStatusBadges({ orientationMode, p1OnRight, p1, p2 }) {
981
417
  const p1OnRightSide = orientationMode === "sideBySide" && p1OnRight;
982
418
  const p2OnLeftSide = orientationMode === "sideBySide" && !p1OnRight;
983
- return /* @__PURE__ */ jsxs14(Fragment5, { children: [
984
- p1 && /* @__PURE__ */ jsx17(View13, { pointerEvents: "none", style: p1OnRightSide ? styles14.bottomRight : styles14.bottomLeft, children: /* @__PURE__ */ jsx17(StatusBadge, { icon: p1.icon }) }),
985
- p2 && /* @__PURE__ */ jsx17(View13, { pointerEvents: "none", style: p2OnLeftSide ? styles14.topLeft : styles14.topRight, children: /* @__PURE__ */ jsx17(StatusBadge, { icon: p2.icon }) })
419
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
420
+ p1 && /* @__PURE__ */ jsx6(View5, { pointerEvents: "none", style: p1OnRightSide ? styles4.bottomRight : styles4.bottomLeft, children: /* @__PURE__ */ jsx6(StatusBadge, { icon: p1.icon }) }),
421
+ p2 && /* @__PURE__ */ jsx6(View5, { pointerEvents: "none", style: p2OnLeftSide ? styles4.topLeft : styles4.topRight, children: /* @__PURE__ */ jsx6(StatusBadge, { icon: p2.icon }) })
986
422
  ] });
987
423
  }
988
- var styles14 = StyleSheet14.create({
424
+ var styles4 = StyleSheet4.create({
989
425
  badge: {
990
426
  alignItems: "center",
991
427
  backgroundColor: "rgba(0,0,0,0.28)",
@@ -1005,11 +441,11 @@ var styles14 = StyleSheet14.create({
1005
441
 
1006
442
  // src/InlineColorPicker.tsx
1007
443
  import { defaultColors, getContrastColor as getContrastColor2 } from "@rific/auto-paper";
1008
- import { TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
1009
- import { clamp, useRotation as useRotation5 } from "@tastic/core";
1010
- import { ScrollView as ScrollView4, StyleSheet as StyleSheet15, useWindowDimensions as useWindowDimensions3, View as View14 } from "react-native";
1011
- import { Icon as Icon7, Text as Text10 } from "react-native-paper";
1012
- import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
444
+ import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
445
+ import { clamp, useRotation as useRotation2 } from "@tastic/core";
446
+ import { ScrollView as ScrollView2, StyleSheet as StyleSheet5, useWindowDimensions as useWindowDimensions2, View as View6 } from "react-native";
447
+ import { Icon as Icon4, Text as Text3 } from "react-native-paper";
448
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1013
449
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
1014
450
  var DEFAULT_SIZE = 48;
1015
451
  var SWATCH_SIZE = 28;
@@ -1031,10 +467,10 @@ function getInlineColorPickerContentSize(swatchCount, windowWidth, columns) {
1031
467
  return { width, height };
1032
468
  }
1033
469
  function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = defaultColors, takenValue, allowSwapTaken, dark, align: forcedAlign, alignOverride, rotation: rotationOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
1034
- const ambientRotation = useRotation5();
470
+ const ambientRotation = useRotation2();
1035
471
  const rotation = rotationOverride ?? ambientRotation;
1036
472
  const menuBg = dark ? "#000000" : "#FFFFFF";
1037
- const { width: windowWidth } = useWindowDimensions3();
473
+ const { width: windowWidth } = useWindowDimensions2();
1038
474
  const { width: swatchesWidth, height: swatchesHeight } = getInlineColorPickerContentSize(swatches.length, windowWidth, columns);
1039
475
  const open = host.openId === id;
1040
476
  const auto = useAutoAlign(open, swatchesWidth, swatchesHeight, rotation);
@@ -1045,15 +481,15 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1045
481
  const displayValue = displayFor(value);
1046
482
  const contrastColor = getContrastColor2(displayValue);
1047
483
  const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
1048
- return /* @__PURE__ */ jsxs15(View14, { style: [styles15.anchor, open && styles15.anchorOpen], children: [
1049
- /* @__PURE__ */ jsx18(TouchableRipple3, { onPress: () => host.toggle(id), borderless: true, style: [styles15.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx18(View14, { ref: triggerRef, collapsable: false, style: styles15.triggerMeasure, children: tag ? /* @__PURE__ */ jsx18(Text10, { style: [styles15.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx18(Icon7, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
1050
- /* @__PURE__ */ jsx18(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx18(ScrollView4, { style: [styles15.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles15.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
484
+ return /* @__PURE__ */ jsxs5(View6, { style: [styles5.anchor, open && styles5.anchorOpen], children: [
485
+ /* @__PURE__ */ jsx7(TouchableRipple2, { onPress: () => host.toggle(id), borderless: true, style: [styles5.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx7(View6, { ref: triggerRef, collapsable: false, style: styles5.triggerMeasure, children: tag ? /* @__PURE__ */ jsx7(Text3, { style: [styles5.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx7(Icon4, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
486
+ /* @__PURE__ */ jsx7(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx7(ScrollView2, { style: [styles5.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles5.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
1051
487
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
1052
488
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
1053
489
  const swappable = taken && allowSwapTaken;
1054
490
  const swatchDisplay = displayFor(swatch.value);
1055
- return /* @__PURE__ */ jsx18(
1056
- TouchableRipple3,
491
+ return /* @__PURE__ */ jsx7(
492
+ TouchableRipple2,
1057
493
  {
1058
494
  disabled: taken && !swappable,
1059
495
  onPress: () => {
@@ -1061,19 +497,19 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1061
497
  if (autoDismiss) host.close();
1062
498
  },
1063
499
  borderless: true,
1064
- style: [styles15.swatch, { backgroundColor: swatchDisplay }, selected && styles15.swatchSelected, taken && !swappable && styles15.swatchTaken],
1065
- children: selected ? /* @__PURE__ */ jsx18(Icon7, { source: "check", size: 16, color: getContrastColor2(swatchDisplay) }) : swappable ? /* @__PURE__ */ jsx18(Icon7, { source: "swap-horizontal", size: 16, color: getContrastColor2(swatchDisplay) }) : taken ? /* @__PURE__ */ jsx18(Icon7, { source: "close", size: 16, color: getContrastColor2(swatchDisplay) }) : /* @__PURE__ */ jsx18(View14, {})
500
+ style: [styles5.swatch, { backgroundColor: swatchDisplay }, selected && styles5.swatchSelected, taken && !swappable && styles5.swatchTaken],
501
+ children: selected ? /* @__PURE__ */ jsx7(Icon4, { source: "check", size: 16, color: getContrastColor2(swatchDisplay) }) : swappable ? /* @__PURE__ */ jsx7(Icon4, { source: "swap-horizontal", size: 16, color: getContrastColor2(swatchDisplay) }) : taken ? /* @__PURE__ */ jsx7(Icon4, { source: "close", size: 16, color: getContrastColor2(swatchDisplay) }) : /* @__PURE__ */ jsx7(View6, {})
1066
502
  },
1067
503
  swatch.value
1068
504
  );
1069
505
  }) }) })
1070
506
  ] });
1071
507
  }
1072
- var styles15 = StyleSheet15.create({
508
+ var styles5 = StyleSheet5.create({
1073
509
  anchor: {
1074
510
  position: "relative"
1075
511
  },
1076
- // See SectionedDropdown's identical comment — React Native Web gives every position:'relative' view
512
+ // See SectionedDropdown's identical comment - React Native Web gives every position:'relative' view
1077
513
  // its own stacking context, so the elevation has to live on the anchor itself, not just the
1078
514
  // popover content nested inside it, to correctly paint above this anchor's own later siblings.
1079
515
  anchorOpen: {
@@ -1101,12 +537,12 @@ var styles15 = StyleSheet15.create({
1101
537
  shadowOffset: { height: 2, width: 0 },
1102
538
  shadowOpacity: 0.3,
1103
539
  shadowRadius: 8
1104
- // width is set inline above — border-box sizing means it has to include the border too
540
+ // width is set inline above - border-box sizing means it has to include the border too
1105
541
  // (2*SWATCHES_BORDER_WIDTH), not just padding+content, or exactly enough room goes missing that
1106
542
  // the last column silently wraps to a new row. Anything wider than the exact sum just shows as
1107
543
  // dead space on the right edge of each row instead.
1108
544
  },
1109
- // Separate from `swatches` itself — see SectionedDropdown's identical menu/menuContent split.
545
+ // Separate from `swatches` itself - see SectionedDropdown's identical menu/menuContent split.
1110
546
  // The scroll frame's own bounds (including maxHeight) live on the ScrollView; the actual grid
1111
547
  // wrapping belongs to its contentContainerStyle, which sizes to the unclamped content instead.
1112
548
  swatchesContent: {
@@ -1119,7 +555,7 @@ var styles15 = StyleSheet15.create({
1119
555
  fontWeight: "700",
1120
556
  letterSpacing: 0.5
1121
557
  },
1122
- // borderRadius/height/width come from the `size` prop instead (see render-site override) — no
558
+ // borderRadius/height/width come from the `size` prop instead (see render-site override) - no
1123
559
  // fixed default here, since this style object is shared by every caller regardless of size.
1124
560
  trigger: {
1125
561
  alignItems: "center",
@@ -1127,7 +563,7 @@ var styles15 = StyleSheet15.create({
1127
563
  justifyContent: "center"
1128
564
  },
1129
565
  // Wraps just the icon/tag, inside the TouchableRipple, purely so useAutoAlign has a plain View
1130
- // to attach its measurement ref to — TouchableRipple itself doesn't forward a ref to a
566
+ // to attach its measurement ref to - TouchableRipple itself doesn't forward a ref to a
1131
567
  // measurable native node. Padded horizontally so a multi-character tag doesn't butt right up
1132
568
  // against the trigger's own circular edge (an icon alone doesn't need it, but this style is
1133
569
  // shared by both).
@@ -1142,10 +578,10 @@ var styles15 = StyleSheet15.create({
1142
578
 
1143
579
  // src/LabeledDropdown.tsx
1144
580
  import { getContrastColor as getContrastColor3 } from "@rific/auto-paper";
1145
- import { TouchableRipple as TouchableRipple4 } from "@rific/feedback-press";
1146
- import { ScrollView as ScrollView5, StyleSheet as StyleSheet16, View as View15 } from "react-native";
1147
- import { Icon as Icon8, Text as Text11 } from "react-native-paper";
1148
- import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
581
+ import { TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
582
+ import { ScrollView as ScrollView3, StyleSheet as StyleSheet6, View as View7 } from "react-native";
583
+ import { Icon as Icon5, Text as Text4 } from "react-native-paper";
584
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1149
585
  var TRIGGER_HEIGHT = 28;
1150
586
  var POPOVER_WIDTH = 180;
1151
587
  var ROW_HEIGHT = 36;
@@ -1168,27 +604,27 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
1168
604
  onChange(option.value);
1169
605
  host.close();
1170
606
  };
1171
- return /* @__PURE__ */ jsxs16(View15, { style: [styles16.anchor, open && styles16.anchorOpen], children: [
1172
- /* @__PURE__ */ jsx19(TouchableRipple4, { onPress: () => host.toggle(id), style: styles16.trigger, children: /* @__PURE__ */ jsxs16(View15, { ref: triggerRef, collapsable: false, style: styles16.triggerInner, children: [
1173
- /* @__PURE__ */ jsx19(Text11, { style: [styles16.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
1174
- /* @__PURE__ */ jsx19(Icon8, { source: "menu-down", size: 16, color })
607
+ return /* @__PURE__ */ jsxs6(View7, { style: [styles6.anchor, open && styles6.anchorOpen], children: [
608
+ /* @__PURE__ */ jsx8(TouchableRipple3, { onPress: () => host.toggle(id), style: styles6.trigger, children: /* @__PURE__ */ jsxs6(View7, { ref: triggerRef, collapsable: false, style: styles6.triggerInner, children: [
609
+ /* @__PURE__ */ jsx8(Text4, { style: [styles6.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
610
+ /* @__PURE__ */ jsx8(Icon5, { source: "menu-down", size: 16, color })
1175
611
  ] }) }),
1176
- /* @__PURE__ */ jsx19(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ jsx19(ScrollView5, { style: [styles16.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles16.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
612
+ /* @__PURE__ */ jsx8(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ jsx8(ScrollView3, { style: [styles6.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles6.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
1177
613
  const isSelected = option.value === value;
1178
614
  const onColor = getContrastColor3(color);
1179
- return /* @__PURE__ */ jsx19(TouchableRipple4, { onPress: () => handleSelect(option), style: [styles16.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs16(View15, { style: styles16.rowInner, children: [
1180
- option.icon && /* @__PURE__ */ jsx19(Icon8, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
1181
- /* @__PURE__ */ jsx19(Text11, { style: [styles16.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
615
+ return /* @__PURE__ */ jsx8(TouchableRipple3, { onPress: () => handleSelect(option), style: [styles6.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs6(View7, { style: styles6.rowInner, children: [
616
+ option.icon && /* @__PURE__ */ jsx8(Icon5, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
617
+ /* @__PURE__ */ jsx8(Text4, { style: [styles6.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
1182
618
  ] }) }, option.value);
1183
619
  }) }) })
1184
620
  ] });
1185
621
  }
1186
- var styles16 = StyleSheet16.create({
622
+ var styles6 = StyleSheet6.create({
1187
623
  anchor: {
1188
624
  position: "relative"
1189
625
  },
1190
626
  // React Native Web gives every position:'relative' view its own stacking context, so the
1191
- // elevation has to live on the anchor itself — see PopoverBody's own stacking-context notes.
627
+ // elevation has to live on the anchor itself - see PopoverBody's own stacking-context notes.
1192
628
  anchorOpen: {
1193
629
  zIndex: 100
1194
630
  },
@@ -1242,21 +678,21 @@ function loadSkiaWeb() {
1242
678
 
1243
679
  // src/PlayerSetupPanel.tsx
1244
680
  import { ProfilePicker } from "@tastic/profile";
1245
- import { StyleSheet as StyleSheet18, View as View16 } from "react-native";
1246
- import { Text as Text13 } from "react-native-paper";
681
+ import { StyleSheet as StyleSheet8, View as View8 } from "react-native";
682
+ import { Text as Text6 } from "react-native-paper";
1247
683
 
1248
684
  // src/ReadyButton.tsx
1249
- import { TouchableRipple as TouchableRipple5 } from "@rific/feedback-press";
1250
- import { StyleSheet as StyleSheet17 } from "react-native";
1251
- import { Text as Text12 } from "react-native-paper";
1252
- import { jsx as jsx20 } from "react/jsx-runtime";
685
+ import { TouchableRipple as TouchableRipple4 } from "@rific/feedback-press";
686
+ import { StyleSheet as StyleSheet7 } from "react-native";
687
+ import { Text as Text5 } from "react-native-paper";
688
+ import { jsx as jsx9 } from "react/jsx-runtime";
1253
689
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
1254
- const readyButtonStyle = [styles17.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
690
+ const readyButtonStyle = [styles7.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
1255
691
  const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
1256
692
  const labelColorStyle = { color: ready ? "#000000" : color };
1257
- return /* @__PURE__ */ jsx20(TouchableRipple5, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx20(Text12, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
693
+ return /* @__PURE__ */ jsx9(TouchableRipple4, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx9(Text5, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
1258
694
  }
1259
- var styles17 = StyleSheet17.create({
695
+ var styles7 = StyleSheet7.create({
1260
696
  readyButton: {
1261
697
  alignItems: "center",
1262
698
  borderRadius: 12,
@@ -1269,18 +705,18 @@ var styles17 = StyleSheet17.create({
1269
705
  });
1270
706
 
1271
707
  // src/usePopoverHost.ts
1272
- import { useCallback as useCallback3, useState as useState5 } from "react";
708
+ import { useCallback as useCallback2, useState as useState2 } from "react";
1273
709
  function usePopoverHost() {
1274
- const [openId, setOpenId] = useState5(null);
1275
- const toggle = useCallback3((id) => {
710
+ const [openId, setOpenId] = useState2(null);
711
+ const toggle = useCallback2((id) => {
1276
712
  setOpenId((prev) => prev === id ? null : id);
1277
713
  }, []);
1278
- const close = useCallback3(() => setOpenId(null), []);
714
+ const close = useCallback2(() => setOpenId(null), []);
1279
715
  return { openId, toggle, close };
1280
716
  }
1281
717
 
1282
718
  // src/PlayerSetupPanel.tsx
1283
- import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
719
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1284
720
  function getColorPopoverId(idPrefix) {
1285
721
  return `${idPrefix}-color`;
1286
722
  }
@@ -1295,27 +731,27 @@ function PlayerSetupPanel({ idPrefix, host, label, labelFontFamily, color, onCol
1295
731
  const showProfilePicker = isHuman && profiles !== void 0 && !!onProfileSelect;
1296
732
  const showCpuDifficulty = !isHuman && cpuDifficulty !== void 0 && cpuDifficultyOptions !== void 0 && !!onCpuDifficultyChange;
1297
733
  const selectedProfile = profiles?.find((p) => p.id === selectedProfileId) ?? null;
1298
- return /* @__PURE__ */ jsxs17(View16, { style: [styles18.panel, ownPopoverOpen && styles18.panelOpen], children: [
1299
- showProfilePicker ? /* @__PURE__ */ jsx21(ProfilePicker, { idPrefix, host: popover, profiles, selectedId: selectedProfileId ?? null, takenId: takenProfileId, color, dark, guestLabel: guestLabel ?? "GUEST", onSelect: (profile) => onProfileSelect?.(profile), onManage: onManageProfiles }) : showCpuDifficulty ? /* @__PURE__ */ jsx21(LabeledDropdown, { id: getCpuDifficultyPopoverId(idPrefix), host: popover, options: cpuDifficultyOptions, value: cpuDifficulty, onChange: onCpuDifficultyChange, color, dark, alignOverride: cpuDifficultyAlignOverride }) : label && /* @__PURE__ */ jsx21(Text13, { variant: "labelSmall", style: { color, fontFamily: labelFontFamily }, children: label }),
1300
- /* @__PURE__ */ jsxs17(View16, { style: [styles18.pickerRow, pickerRowPopoverOpen && styles18.pickerRowOpen], children: [
1301
- /* @__PURE__ */ jsx21(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 }),
734
+ return /* @__PURE__ */ jsxs7(View8, { style: [styles8.panel, ownPopoverOpen && styles8.panelOpen], children: [
735
+ showProfilePicker ? /* @__PURE__ */ jsx10(ProfilePicker, { idPrefix, host: popover, profiles, selectedId: selectedProfileId ?? null, takenId: takenProfileId, color, dark, guestLabel: guestLabel ?? "GUEST", onSelect: (profile) => onProfileSelect?.(profile), onManage: onManageProfiles }) : showCpuDifficulty ? /* @__PURE__ */ jsx10(LabeledDropdown, { id: getCpuDifficultyPopoverId(idPrefix), host: popover, options: cpuDifficultyOptions, value: cpuDifficulty, onChange: onCpuDifficultyChange, color, dark, alignOverride: cpuDifficultyAlignOverride }) : label && /* @__PURE__ */ jsx10(Text6, { variant: "labelSmall", style: { color, fontFamily: labelFontFamily }, children: label }),
736
+ /* @__PURE__ */ jsxs7(View8, { style: [styles8.pickerRow, pickerRowPopoverOpen && styles8.pickerRowOpen], children: [
737
+ /* @__PURE__ */ jsx10(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 }),
1302
738
  secondPicker
1303
739
  ] }),
1304
- isHuman && showReadyButton && onToggleReady && /* @__PURE__ */ jsx21(ReadyButton, { color, ready: ready ?? false, onToggleReady })
740
+ isHuman && showReadyButton && onToggleReady && /* @__PURE__ */ jsx10(ReadyButton, { color, ready: ready ?? false, onToggleReady })
1305
741
  ] });
1306
742
  }
1307
- var styles18 = StyleSheet18.create({
743
+ var styles8 = StyleSheet8.create({
1308
744
  panel: {
1309
745
  alignItems: "center",
1310
746
  gap: 12
1311
747
  },
1312
- // See SectionedDropdown's anchorOpen comment — this panel is typically a sibling of another
748
+ // See SectionedDropdown's anchorOpen comment - this panel is typically a sibling of another
1313
749
  // panel (and/or a shared-controls row), so a popover escaping this panel's bounds needs the panel
1314
750
  // itself elevated, not just the popover content, to paint above a later sibling.
1315
751
  panelOpen: {
1316
752
  zIndex: 100
1317
753
  },
1318
- // Same gap as `panel`'s own vertical rhythm, reused horizontally — the color picker and (when
754
+ // Same gap as `panel`'s own vertical rhythm, reused horizontally - the color picker and (when
1319
755
  // present) secondPicker sit side by side within this row instead of stacked in the panel's own
1320
756
  // column.
1321
757
  pickerRow: {
@@ -1323,10 +759,10 @@ var styles18 = StyleSheet18.create({
1323
759
  flexDirection: "row",
1324
760
  gap: 12
1325
761
  },
1326
- // A popover is an absolutely-positioned sibling of its own trigger — so a tall one (the color
762
+ // A popover is an absolutely-positioned sibling of its own trigger - so a tall one (the color
1327
763
  // swatch grid) can extend down far enough to overlap the ReadyButton below, which paints on top of
1328
764
  // it by default DOM order since it's a later sibling within `panel`. `panel`'s own panelOpen
1329
- // elevation only helps this panel paint above OTHER panels (see its own comment) — it does nothing
765
+ // elevation only helps this panel paint above OTHER panels (see its own comment) - it does nothing
1330
766
  // for stacking *within* this panel, since React Native Web gives each View its own stacking
1331
767
  // context. This is what actually fixes that: elevate pickerRow itself above its own later sibling
1332
768
  // whenever one of its own popovers is open.
@@ -1336,29 +772,29 @@ var styles18 = StyleSheet18.create({
1336
772
  });
1337
773
 
1338
774
  // src/PressAwayOverlay.tsx
1339
- import { Pressable, StyleSheet as StyleSheet19 } from "react-native";
1340
- import { jsx as jsx22 } from "react/jsx-runtime";
775
+ import { Pressable, StyleSheet as StyleSheet9 } from "react-native";
776
+ import { jsx as jsx11 } from "react/jsx-runtime";
1341
777
  function PressAwayOverlay({ active, onPress, style }) {
1342
778
  if (!active) return null;
1343
- return /* @__PURE__ */ jsx22(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet19.absoluteFill, style] });
779
+ return /* @__PURE__ */ jsx11(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet9.absoluteFill, style] });
1344
780
  }
1345
781
 
1346
782
  // src/SharedActionBand.tsx
1347
- import { IconButton as IconButton4 } from "@rific/feedback-press";
1348
- import { StyleSheet as StyleSheet20, View as View17 } from "react-native";
1349
- import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
783
+ import { IconButton as IconButton2 } from "@rific/feedback-press";
784
+ import { StyleSheet as StyleSheet10, View as View9 } from "react-native";
785
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
1350
786
  function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
1351
- return /* @__PURE__ */ jsxs18(View17, { style: [styles19.column, popoverOpen && styles19.columnOpen], children: [
1352
- /* @__PURE__ */ jsxs18(View17, { style: styles19.actionsRow, children: [
1353
- /* @__PURE__ */ jsx23(IconButton4, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1354
- onRandomize && /* @__PURE__ */ jsx23(IconButton4, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1355
- onReset && /* @__PURE__ */ jsx23(IconButton4, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
1356
- /* @__PURE__ */ jsx23(IconButton4, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
787
+ return /* @__PURE__ */ jsxs8(View9, { style: [styles9.column, popoverOpen && styles9.columnOpen], children: [
788
+ /* @__PURE__ */ jsxs8(View9, { style: styles9.actionsRow, children: [
789
+ /* @__PURE__ */ jsx12(IconButton2, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
790
+ onRandomize && /* @__PURE__ */ jsx12(IconButton2, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
791
+ onReset && /* @__PURE__ */ jsx12(IconButton2, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
792
+ /* @__PURE__ */ jsx12(IconButton2, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
1357
793
  ] }),
1358
794
  children
1359
795
  ] });
1360
796
  }
1361
- var styles19 = StyleSheet20.create({
797
+ var styles9 = StyleSheet10.create({
1362
798
  actionsRow: {
1363
799
  alignItems: "center",
1364
800
  flexDirection: "row",
@@ -1373,86 +809,45 @@ var styles19 = StyleSheet20.create({
1373
809
  }
1374
810
  });
1375
811
 
1376
- // src/StaggeredWord.tsx
1377
- import { useEffect as useEffect2 } from "react";
1378
- import { StyleSheet as StyleSheet21, View as View18 } from "react-native";
1379
- import Animated, { Easing, useAnimatedStyle, useSharedValue, withDelay, withTiming } from "react-native-reanimated";
1380
- import { jsx as jsx24 } from "react/jsx-runtime";
1381
- var STAGGER_MS = 45;
1382
- var LETTER_DURATION_MS = 320;
1383
- function getStaggeredWordDuration(letterCount) {
1384
- return (letterCount - 1) * STAGGER_MS + LETTER_DURATION_MS;
1385
- }
1386
- function AnimatedLetter({ char, index, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }) {
1387
- const progress = useSharedValue(reducedMotion ? 1 : 0);
1388
- useEffect2(() => {
1389
- if (reducedMotion) return;
1390
- progress.value = withDelay(index * STAGGER_MS, withTiming(1, { duration: LETTER_DURATION_MS, easing: Easing.out(Easing.back(1.5)) }));
1391
- }, [reducedMotion]);
1392
- const style = useAnimatedStyle(() => ({
1393
- opacity: progress.value,
1394
- transform: [{ translateY: (1 - progress.value) * 12 }, { scale: 0.85 + progress.value * 0.15 }]
1395
- }));
1396
- return /* @__PURE__ */ jsx24(Animated.Text, { allowFontScaling: false, style: [{ color, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, style], children: char });
1397
- }
1398
- function StaggeredWord({ word, color, fontFamily, fontSize, lineHeight, reducedMotion, startIndex = 0, fontWeight = "bold", letterSpacing, style, onLayout }) {
1399
- return /* @__PURE__ */ jsx24(View18, { style: [styles20.row, style], onLayout, children: word.split("").map((char, i) => /* @__PURE__ */ jsx24(AnimatedLetter, { char, index: startIndex + i, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, i)) });
1400
- }
1401
- var styles20 = StyleSheet21.create({
1402
- row: {
1403
- flexDirection: "row"
1404
- }
1405
- });
1406
-
1407
- // src/UpdateDialog.tsx
1408
- import { useUpdater as useUpdater2 } from "@rific/updater";
1409
- import { jsx as jsx25 } from "react/jsx-runtime";
1410
- function UpdateDialog({ rotation = 0, autoCheck = true, autoPrompt = true, onError }) {
1411
- const { manifest, onConfirm, respond } = useUpdateConfirmBridge();
1412
- useUpdater2({ autoCheck, autoPrompt, onConfirm, onError });
1413
- const date = manifest ? new Date(manifest.createdAt) : null;
1414
- return /* @__PURE__ */ jsx25(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) });
1415
- }
1416
-
1417
812
  // src/useQuitConfirmation.ts
1418
- import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef3, useState as useState6 } from "react";
813
+ import { useCallback as useCallback3, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
1419
814
  function useQuitConfirmation(hasProgress, onConfirmedBack) {
1420
- const [confirmVisible, setConfirmVisible] = useState6(false);
1421
- const hasProgressRef = useRef3(hasProgress);
1422
- useEffect3(() => {
815
+ const [confirmVisible, setConfirmVisible] = useState3(false);
816
+ const hasProgressRef = useRef2(hasProgress);
817
+ useEffect2(() => {
1423
818
  hasProgressRef.current = hasProgress;
1424
819
  });
1425
- const onConfirmedBackRef = useRef3(onConfirmedBack);
1426
- useEffect3(() => {
820
+ const onConfirmedBackRef = useRef2(onConfirmedBack);
821
+ useEffect2(() => {
1427
822
  onConfirmedBackRef.current = onConfirmedBack;
1428
823
  });
1429
- const requestBack = useCallback4(() => {
824
+ const requestBack = useCallback3(() => {
1430
825
  if (hasProgressRef.current()) setConfirmVisible(true);
1431
826
  else onConfirmedBackRef.current();
1432
827
  }, []);
1433
- const cancelBack = useCallback4(() => setConfirmVisible(false), []);
828
+ const cancelBack = useCallback3(() => setConfirmVisible(false), []);
1434
829
  return { confirmVisible, requestBack, cancelBack };
1435
830
  }
1436
831
 
1437
832
  // src/useZoneClampedAlign.ts
1438
833
  import { useZoneBounds } from "@tastic/split-screen";
1439
- import { useEffect as useEffect4, useState as useState7 } from "react";
1440
- import { useWindowDimensions as useWindowDimensions4 } from "react-native";
1441
- import { useSafeAreaInsets as useSafeAreaInsets2 } from "react-native-safe-area-context";
834
+ import { useEffect as useEffect3, useState as useState4 } from "react";
835
+ import { useWindowDimensions as useWindowDimensions3 } from "react-native";
836
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
1442
837
  var EDGE_MARGIN2 = 12;
1443
838
  function useZoneClampedAlign(open, contentWidth, contentHeight, rotation = 0) {
1444
839
  const auto = useAutoAlign(open, contentWidth, contentHeight, rotation);
1445
840
  const zone = useZoneBounds();
1446
- const { height: windowHeight } = useWindowDimensions4();
1447
- const insets = useSafeAreaInsets2();
841
+ const { height: windowHeight } = useWindowDimensions3();
842
+ const insets = useSafeAreaInsets();
1448
843
  const shouldMeasure = open && auto.measured && zone !== null;
1449
- const [ownBounds, setOwnBounds] = useState7(null);
1450
- const [prevShouldMeasure, setPrevShouldMeasure] = useState7(shouldMeasure);
844
+ const [ownBounds, setOwnBounds] = useState4(null);
845
+ const [prevShouldMeasure, setPrevShouldMeasure] = useState4(shouldMeasure);
1451
846
  if (shouldMeasure !== prevShouldMeasure) {
1452
847
  setPrevShouldMeasure(shouldMeasure);
1453
848
  setOwnBounds(null);
1454
849
  }
1455
- useEffect4(() => {
850
+ useEffect3(() => {
1456
851
  if (!shouldMeasure) return;
1457
852
  auto.triggerRef.current?.measureInWindow((_x, y, _width, height) => {
1458
853
  const belowShared = zone.zoneSide === "belowShared";