@tastic/hud 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,6 +1,11 @@
1
1
  import {
2
2
  SkiaGate
3
3
  } from "./chunk-XEOFR236.mjs";
4
+ import {
5
+ getOverlayCardColors,
6
+ overlayActionStyles,
7
+ overlayStyles
8
+ } from "./chunk-32U2OIRW.mjs";
4
9
 
5
10
  // src/AchievementCatalogSection.tsx
6
11
  import { useAutoPaperTheme as useAutoPaperTheme2 } from "@rific/auto-paper";
@@ -97,21 +102,65 @@ var styles2 = StyleSheet2.create({
97
102
  }
98
103
  });
99
104
 
100
- // src/StatRow.tsx
101
- import { useAutoPaperTheme as useAutoPaperTheme3 } from "@rific/auto-paper";
105
+ // src/AchievementUnlockList.tsx
106
+ import { getContrastColor, useAutoPaperTheme as useAutoPaperTheme3 } from "@rific/auto-paper";
107
+ import { ProfileChip } from "@tastic/profile";
102
108
  import { StyleSheet as StyleSheet3, View as View2 } from "react-native";
103
- import { Text as Text3 } from "react-native-paper";
109
+ import { Icon as Icon2, Text as Text3 } from "react-native-paper";
104
110
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
105
- function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }) {
111
+ var AVATAR_SIZE = 18;
112
+ function AchievementUnlockList({ unlocks, seats, owners, tierColors, fg: fgOverride, style }) {
106
113
  const { dark } = useAutoPaperTheme3();
107
114
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
115
+ const rows = seats.flatMap((seat) => (unlocks[seat] ?? []).map((achievement) => ({ seat, achievement })));
116
+ if (rows.length === 0) return null;
117
+ return /* @__PURE__ */ jsx3(View2, { style: [styles3.list, style], children: rows.map(({ seat, achievement }) => {
118
+ 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 })
123
+ ] }, `${seat}-${achievement.id}`);
124
+ }) });
125
+ }
126
+ var styles3 = StyleSheet3.create({
127
+ circle: {
128
+ alignItems: "center",
129
+ borderRadius: AVATAR_SIZE / 2,
130
+ height: AVATAR_SIZE,
131
+ justifyContent: "center",
132
+ width: AVATAR_SIZE
133
+ },
134
+ label: {
135
+ flexShrink: 1,
136
+ fontSize: 12,
137
+ fontWeight: "700"
138
+ },
139
+ list: {
140
+ gap: 6
141
+ },
142
+ row: {
143
+ alignItems: "center",
144
+ flexDirection: "row",
145
+ gap: 6
146
+ }
147
+ });
148
+
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");
108
157
  const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
109
- return /* @__PURE__ */ jsxs3(View2, { style: styles3.row, children: [
110
- /* @__PURE__ */ jsx3(Text3, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
111
- /* @__PURE__ */ jsx3(Text3, { variant: "bodyMedium", style: [styles3.value, { color: fg }], children: value })
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 })
112
161
  ] });
113
162
  }
114
- var styles3 = StyleSheet3.create({
163
+ var styles4 = StyleSheet4.create({
115
164
  row: {
116
165
  alignItems: "center",
117
166
  flexDirection: "row",
@@ -123,20 +172,20 @@ var styles3 = StyleSheet3.create({
123
172
  });
124
173
 
125
174
  // src/StatSection.tsx
126
- import { useAutoPaperTheme as useAutoPaperTheme4 } from "@rific/auto-paper";
127
- import { StyleSheet as StyleSheet4, View as View3 } from "react-native";
128
- import { Text as Text4 } from "react-native-paper";
129
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
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";
130
179
  function StatSection({ label, fg: fgOverride, sectionBg: sectionBgOverride, children }) {
131
- const { dark } = useAutoPaperTheme4();
180
+ const { dark } = useAutoPaperTheme5();
132
181
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
133
182
  const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
134
- return /* @__PURE__ */ jsxs4(View3, { style: [styles4.section, { backgroundColor: sectionBg }], children: [
135
- /* @__PURE__ */ jsx4(Text4, { variant: "labelMedium", style: [styles4.label, { color: fg }], children: label }),
183
+ return /* @__PURE__ */ jsxs5(View4, { style: [styles5.section, { backgroundColor: sectionBg }], children: [
184
+ /* @__PURE__ */ jsx5(Text5, { variant: "labelMedium", style: [styles5.label, { color: fg }], children: label }),
136
185
  children
137
186
  ] });
138
187
  }
139
- var styles4 = StyleSheet4.create({
188
+ var styles5 = StyleSheet5.create({
140
189
  // Bold and full-contrast (fg), not the original fontless/fgMuted treatment — that read at the
141
190
  // same visual weight as StatRow's own row labels below it (both used the identical muted color;
142
191
  // letterSpacing/variant alone didn't separate them enough to read as "this is the group header"
@@ -153,121 +202,49 @@ var styles4 = StyleSheet4.create({
153
202
  });
154
203
 
155
204
  // src/ActivityStatSection.tsx
156
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
205
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
157
206
  function ActivityStatSection({ stats }) {
158
- return /* @__PURE__ */ jsxs5(StatSection, { label: "ACTIVITY", children: [
159
- /* @__PURE__ */ jsx5(StatRow, { label: "Days Played", value: String(stats.distinctDaysPlayed) }),
160
- /* @__PURE__ */ jsx5(StatRow, { label: "Day Streak", value: String(stats.currentDayStreak) }),
161
- /* @__PURE__ */ jsx5(StatRow, { label: "Best Day Streak", value: String(stats.bestDayStreak) })
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) })
162
211
  ] });
163
212
  }
164
213
 
165
214
  // src/BaseSettingsDialog.tsx
166
- import { AutoAppearancePicker, Dialog, useAutoPaperTheme as useAutoPaperTheme6 } from "@rific/auto-paper";
215
+ import { AutoAppearancePicker, Dialog, useAutoPaperTheme as useAutoPaperTheme7 } from "@rific/auto-paper";
167
216
  import { Button as Button2, SoundContext, TouchableRipple, useHapticSettings, useSoundSettings, useVibration } from "@rific/feedback-press";
168
217
  import { useUpdater } from "@rific/updater";
169
218
  import { toRotationStyle as toRotationStyle2, useIsTouchPrimaryDevice, useRotation } from "@tastic/core";
170
219
  import { useContext, useState as useState2 } from "react";
171
- import { Platform as Platform2, ScrollView, StyleSheet as StyleSheet7, View as View5 } from "react-native";
172
- import { Icon as Icon3, Text as Text6 } from "react-native-paper";
220
+ import { Platform as Platform2, ScrollView, StyleSheet as StyleSheet7, View as View6 } from "react-native";
221
+ import { Icon as Icon4, Text as Text7 } from "react-native-paper";
173
222
 
174
223
  // src/ConfirmDialog.tsx
175
- import { useAutoPaperTheme as useAutoPaperTheme5 } from "@rific/auto-paper";
224
+ import { useAutoPaperTheme as useAutoPaperTheme6 } from "@rific/auto-paper";
176
225
  import { Button } from "@rific/feedback-press";
177
226
  import { toRotationStyle } from "@tastic/core";
178
- import { StyleSheet as StyleSheet6, View as View4 } from "react-native";
179
- import { Icon as Icon2, Portal, Text as Text5 } from "react-native-paper";
180
-
181
- // src/overlayCard.ts
182
- import { StyleSheet as StyleSheet5 } from "react-native";
183
- function getOverlayCardColors(dark) {
184
- return {
185
- fg: dark ? "#FFFFFF" : "#000000",
186
- fgMuted: dark ? "rgba(255,255,255,0.4)" : "rgba(0,0,0,0.4)",
187
- cardBg: dark ? "#111111" : "#F2F2F2",
188
- cardBorder: dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)"
189
- };
190
- }
191
- var overlayStyles = StyleSheet5.create({
192
- card: {
193
- alignItems: "center",
194
- borderRadius: 20,
195
- borderWidth: 1,
196
- gap: 16,
197
- maxWidth: 360,
198
- padding: 32
199
- },
200
- overlay: {
201
- alignItems: "center",
202
- backgroundColor: "rgba(0,0,0,0.72)",
203
- bottom: 0,
204
- justifyContent: "center",
205
- left: 0,
206
- position: "absolute",
207
- right: 0,
208
- top: 0
209
- }
210
- });
211
-
212
- // src/ConfirmDialog.tsx
213
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
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";
214
230
  function ConfirmDialog({ visible, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, rotation = 0, icon = "help-circle-outline", destructive = false }) {
215
- const { dark, colors } = useAutoPaperTheme5();
231
+ const { dark, colors } = useAutoPaperTheme6();
216
232
  if (!visible) return null;
217
233
  const { fg, cardBg, cardBorder } = getOverlayCardColors(dark);
218
234
  const accent = destructive ? colors.danger : colors.primary;
219
235
  const onAccent = destructive ? colors.onDanger : colors.onPrimary;
220
236
  const showCancel = cancelLabel !== void 0 && onCancel !== void 0;
221
- return /* @__PURE__ */ jsx6(Portal, { children: /* @__PURE__ */ jsx6(View4, { style: [overlayStyles.overlay, styles5.overlay], children: /* @__PURE__ */ jsxs6(View4, { style: [overlayStyles.card, styles5.card, { backgroundColor: cardBg, borderColor: cardBorder }, toRotationStyle(rotation)], children: [
222
- /* @__PURE__ */ jsx6(Icon2, { source: icon, size: 64, color: accent }),
223
- /* @__PURE__ */ jsx6(Text5, { variant: "headlineLarge", style: [styles5.title, { color: accent }], children: title }),
224
- /* @__PURE__ */ jsx6(Text5, { variant: "bodyLarge", style: [styles5.body, { color: fg }], children: message }),
225
- /* @__PURE__ */ jsxs6(View4, { style: styles5.actions, children: [
226
- showCancel && /* @__PURE__ */ jsx6(Button, { mode: "outlined", onPress: onCancel, style: styles5.actionButton, children: cancelLabel }),
227
- /* @__PURE__ */ jsx6(Button, { mode: "contained", onPress: onConfirm, style: showCancel ? styles5.actionButton : styles5.actionButtonSingle, buttonColor: accent, textColor: onAccent, children: confirmLabel })
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 })
228
244
  ] })
229
245
  ] }) }) });
230
246
  }
231
- var styles5 = StyleSheet6.create({
232
- // minWidth (not flexBasis) is each button's real floor - whenever both fit side by side, flexGrow
233
- // still splits any leftover width evenly beyond that floor, same visual result as a plain flex: 1
234
- // on a roomy screen, but a button can never be squeezed narrower than 128, only wrap to its own
235
- // row instead (see actions' flexWrap below). This used to be flexBasis: 128, which measured
236
- // correctly on web but rendered every button roughly SQUARE on iOS - confirmed live (see the
237
- // "Quit Match?" Cancel/Quit buttons and the single-button "OK" dialogs both coming out ~128pt
238
- // tall instead of wide). Root cause: react-native-paper's Button renders its Surface as two
239
- // nested views on iOS only (Surface.tsx's SurfaceIOS, for shadow rendering) - only a curated
240
- // allowlist of style keys (flex/flexGrow/flexShrink/width/height/position/... - notably NOT
241
- // flexBasis) reaches the outer view, which is the real flex item inside this row; every other
242
- // key, flexBasis included, falls through to an inner view whose own unspecified flexDirection
243
- // defaults to column. flexGrow: 1 landed on the (row-context) outer view and grew width
244
- // correctly; flexBasis: 128 landed on the (column-context) inner view instead, where a flexBasis
245
- // sets HEIGHT, not width. minWidth isn't axis-relative like flexBasis, so it constrains width
246
- // correctly regardless of which of the two views it ends up on. react-native-paper's Button also
247
- // hardcodes numberOfLines={1} on its label with no override, so a button squeezed under its
248
- // label's natural width silently ellipsizes ("Cancel" -> "Can...") instead of wrapping - assumed
249
- // fixable by just giving the row more width, until confirmed live that these two-word labels
250
- // (Cancel/Reset/New Game) can still lose the truncation fight even with the extra room card's own
251
- // width fix below provides, on a narrow enough screen.
252
- actionButton: {
253
- flexGrow: 1,
254
- minWidth: 128
255
- },
256
- // A lone confirm button (no cancel - BaseSettingsDialog's info-only "OK" dialogs, e.g. the
257
- // update-check "no update found" message) has nothing left to share actions' width with, so
258
- // actionButton's own flexGrow: 1 would stretch it across the entire row instead of reading as a
259
- // normal button. Same 128 floor, no flexGrow, so it stays at that natural size instead of filling
260
- // the card - centered by actions' own justifyContent below.
261
- actionButtonSingle: {
262
- minWidth: 128
263
- },
264
- actions: {
265
- flexDirection: "row",
266
- flexWrap: "wrap",
267
- gap: 12,
268
- justifyContent: "center",
269
- width: "100%"
270
- },
247
+ var styles6 = StyleSheet6.create({
271
248
  body: {
272
249
  textAlign: "center"
273
250
  },
@@ -316,12 +293,12 @@ function useUpdateConfirmBridge() {
316
293
  }
317
294
 
318
295
  // src/BaseSettingsDialog.tsx
319
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
296
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
320
297
  function SettingIcon({ source, color, containerColor }) {
321
- return /* @__PURE__ */ jsx7(View5, { style: [styles6.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ jsx7(Icon3, { source, size: 18, color }) });
298
+ return /* @__PURE__ */ jsx8(View6, { style: [styles7.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ jsx8(Icon4, { source, size: 18, color }) });
322
299
  }
323
- function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, children }) {
324
- const { colors } = useAutoPaperTheme6();
300
+ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, onShowHowToPlay, children }) {
301
+ const { colors } = useAutoPaperTheme7();
325
302
  const ambientRotation = useRotation();
326
303
  const rotation = rotationOverride ?? ambientRotation;
327
304
  const isTouchPrimary = useIsTouchPrimaryDevice();
@@ -345,27 +322,27 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
345
322
  const showSound = !hideSound;
346
323
  const showAppearance = !hideAppearance;
347
324
  const showUpdateCheck = Platform2.OS !== "web" && !hideUpdateCheck;
348
- return /* @__PURE__ */ jsxs7(Fragment2, { children: [
349
- /* @__PURE__ */ jsxs7(Dialog, { visible, onDismiss, style: [styles6.dialog, toRotationStyle2(rotation)], children: [
350
- /* @__PURE__ */ jsx7(Dialog.Title, { children: "Settings" }),
351
- /* @__PURE__ */ jsx7(Dialog.ScrollArea, { style: styles6.scrollArea, children: /* @__PURE__ */ jsxs7(ScrollView, { contentContainerStyle: styles6.content, showsVerticalScrollIndicator: false, children: [
352
- /* @__PURE__ */ jsxs7(View5, { style: styles6.toggleGroup, children: [
353
- showLockOrientation && /* @__PURE__ */ jsx7(TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles6.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ jsxs7(View5, { style: styles6.toggleContent, children: [
354
- /* @__PURE__ */ jsx7(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
355
- /* @__PURE__ */ jsxs7(View5, { style: styles6.flexShrink, children: [
356
- /* @__PURE__ */ jsx7(Text6, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
357
- /* @__PURE__ */ jsx7(Text6, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
325
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
326
+ /* @__PURE__ */ jsxs8(Dialog, { visible, onDismiss, style: [styles7.dialog, toRotationStyle2(rotation)], children: [
327
+ /* @__PURE__ */ jsx8(Dialog.Title, { children: "Settings" }),
328
+ /* @__PURE__ */ jsx8(Dialog.ScrollArea, { style: styles7.scrollArea, children: /* @__PURE__ */ jsxs8(ScrollView, { contentContainerStyle: styles7.content, showsVerticalScrollIndicator: false, children: [
329
+ /* @__PURE__ */ jsxs8(View6, { style: styles7.toggleGroup, children: [
330
+ showLockOrientation && /* @__PURE__ */ jsx8(TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles7.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ jsxs8(View6, { style: styles7.toggleContent, children: [
331
+ /* @__PURE__ */ jsx8(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
332
+ /* @__PURE__ */ jsxs8(View6, { style: styles7.flexShrink, children: [
333
+ /* @__PURE__ */ jsx8(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
334
+ /* @__PURE__ */ jsx8(Text7, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
358
335
  ] })
359
336
  ] }) }),
360
- showEdgeGuard && /* @__PURE__ */ jsx7(TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles6.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ jsxs7(View5, { style: styles6.toggleContent, children: [
361
- /* @__PURE__ */ jsx7(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
362
- /* @__PURE__ */ jsxs7(View5, { style: styles6.flexShrink, children: [
363
- /* @__PURE__ */ jsx7(Text6, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
364
- /* @__PURE__ */ jsx7(Text6, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
337
+ showEdgeGuard && /* @__PURE__ */ jsx8(TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles7.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ jsxs8(View6, { style: styles7.toggleContent, children: [
338
+ /* @__PURE__ */ jsx8(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
339
+ /* @__PURE__ */ jsxs8(View6, { style: styles7.flexShrink, children: [
340
+ /* @__PURE__ */ jsx8(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
341
+ /* @__PURE__ */ jsx8(Text7, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
365
342
  ] })
366
343
  ] }) }),
367
- (showSound || showHaptics) && /* @__PURE__ */ jsxs7(View5, { style: styles6.toggleRow, children: [
368
- showSound && /* @__PURE__ */ jsx7(
344
+ (showSound || showHaptics) && /* @__PURE__ */ jsxs8(View6, { style: styles7.toggleRow, children: [
345
+ showSound && /* @__PURE__ */ jsx8(
369
346
  TouchableRipple,
370
347
  {
371
348
  soundDisabled: true,
@@ -374,15 +351,15 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
374
351
  setSoundSettings({ enabled });
375
352
  if (enabled) sound.selection?.();
376
353
  },
377
- style: styles6.toggleButtonInRow,
354
+ style: styles7.toggleButtonInRow,
378
355
  accessibilityLabel: `Sound ${soundSettings.enabled ? "on" : "off"}`,
379
- children: /* @__PURE__ */ jsxs7(View5, { style: styles6.toggleContent, children: [
380
- /* @__PURE__ */ jsx7(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
381
- /* @__PURE__ */ jsx7(Text6, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
356
+ children: /* @__PURE__ */ jsxs8(View6, { style: styles7.toggleContent, children: [
357
+ /* @__PURE__ */ jsx8(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
358
+ /* @__PURE__ */ jsx8(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
382
359
  ] })
383
360
  }
384
361
  ),
385
- showHaptics && /* @__PURE__ */ jsx7(
362
+ showHaptics && /* @__PURE__ */ jsx8(
386
363
  TouchableRipple,
387
364
  {
388
365
  hapticDisabled: true,
@@ -391,36 +368,54 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
391
368
  setHapticSettings({ vibrate });
392
369
  if (vibrate) forceHapticFeedback();
393
370
  },
394
- style: styles6.toggleButtonInRow,
371
+ style: styles7.toggleButtonInRow,
395
372
  accessibilityLabel: `Haptics ${hapticSettings.vibrate ? "on" : "off"}`,
396
- children: /* @__PURE__ */ jsxs7(View5, { style: styles6.toggleContent, children: [
397
- /* @__PURE__ */ jsx7(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
398
- /* @__PURE__ */ jsx7(Text6, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
373
+ children: /* @__PURE__ */ jsxs8(View6, { style: styles7.toggleContent, children: [
374
+ /* @__PURE__ */ jsx8(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
375
+ /* @__PURE__ */ jsx8(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
399
376
  ] })
400
377
  }
401
378
  )
402
- ] })
379
+ ] }),
380
+ onShowHowToPlay && /* @__PURE__ */ jsx8(
381
+ TouchableRipple,
382
+ {
383
+ onPress: () => {
384
+ onDismiss();
385
+ onShowHowToPlay();
386
+ },
387
+ style: styles7.toggleButton,
388
+ accessibilityLabel: "How to Play",
389
+ children: /* @__PURE__ */ jsxs8(View6, { style: styles7.toggleContent, children: [
390
+ /* @__PURE__ */ jsx8(SettingIcon, { source: "help-circle-outline", color: colors.secondary, containerColor: colors.secondaryContainer }),
391
+ /* @__PURE__ */ jsxs8(View6, { style: styles7.flexShrink, children: [
392
+ /* @__PURE__ */ jsx8(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "How to Play" }),
393
+ /* @__PURE__ */ jsx8(Text7, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Replay the quick intro" })
394
+ ] })
395
+ ] })
396
+ }
397
+ )
403
398
  ] }),
404
- showAppearance && /* @__PURE__ */ jsxs7(View5, { style: styles6.section, children: [
405
- /* @__PURE__ */ jsx7(Text6, { variant: "labelMedium", style: [styles6.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
406
- /* @__PURE__ */ jsx7(AutoAppearancePicker, { showLabels: false })
399
+ showAppearance && /* @__PURE__ */ jsxs8(View6, { style: styles7.section, children: [
400
+ /* @__PURE__ */ jsx8(Text7, { variant: "labelMedium", style: [styles7.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
401
+ /* @__PURE__ */ jsx8(AutoAppearancePicker, { showLabels: false })
407
402
  ] }),
408
403
  children,
409
- showUpdateCheck && /* @__PURE__ */ jsxs7(View5, { style: styles6.section, children: [
410
- /* @__PURE__ */ jsxs7(Text6, { variant: "labelSmall", style: [styles6.sectionLabel, { color: colors.onSurfaceVariant }], children: [
404
+ showUpdateCheck && /* @__PURE__ */ jsxs8(View6, { style: styles7.section, children: [
405
+ /* @__PURE__ */ jsxs8(Text7, { variant: "labelSmall", style: [styles7.sectionLabel, { color: colors.onSurfaceVariant }], children: [
411
406
  "VERSION ",
412
407
  version,
413
408
  updateReady ? " \xB7 UPDATE READY" : ""
414
409
  ] }),
415
- /* @__PURE__ */ jsx7(Button2, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
410
+ /* @__PURE__ */ jsx8(Button2, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
416
411
  ] })
417
412
  ] }) })
418
413
  ] }),
419
- /* @__PURE__ */ jsx7(ConfirmDialog, { visible: !!updateManifest, title: "Update Available", message: updateManifestDate ? `Released ${updateManifestDate.toLocaleDateString()} at ${updateManifestDate.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation, onConfirm: () => respondToUpdateConfirm(true), onCancel: () => respondToUpdateConfirm(false) }),
420
- /* @__PURE__ */ jsx7(ConfirmDialog, { visible: !!infoMessage, title: infoMessage?.title ?? "", message: infoMessage?.message ?? "", confirmLabel: "OK", icon: "information-outline", rotation, onConfirm: () => setInfoMessage(null) })
414
+ /* @__PURE__ */ jsx8(ConfirmDialog, { visible: !!updateManifest, title: "Update Available", message: updateManifestDate ? `Released ${updateManifestDate.toLocaleDateString()} at ${updateManifestDate.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation, onConfirm: () => respondToUpdateConfirm(true), onCancel: () => respondToUpdateConfirm(false) }),
415
+ /* @__PURE__ */ jsx8(ConfirmDialog, { visible: !!infoMessage, title: infoMessage?.title ?? "", message: infoMessage?.message ?? "", confirmLabel: "OK", icon: "information-outline", rotation, onConfirm: () => setInfoMessage(null) })
421
416
  ] });
422
417
  }
423
- var styles6 = StyleSheet7.create({
418
+ var styles7 = StyleSheet7.create({
424
419
  content: {
425
420
  gap: 24,
426
421
  paddingVertical: 20
@@ -488,31 +483,31 @@ var styles6 = StyleSheet7.create({
488
483
  });
489
484
 
490
485
  // src/BaseStatsScreen.tsx
491
- import { useAutoPaperTheme as useAutoPaperTheme7 } from "@rific/auto-paper";
486
+ import { useAutoPaperTheme as useAutoPaperTheme8 } from "@rific/auto-paper";
492
487
  import { Button as Button3, IconButton } from "@rific/feedback-press";
493
488
  import { useRotation as useRotation2 } from "@tastic/core";
494
489
  import { useState as useState3 } from "react";
495
- import { ScrollView as ScrollView2, StyleSheet as StyleSheet8, View as View6 } from "react-native";
496
- import { Text as Text7 } from "react-native-paper";
497
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
490
+ import { ScrollView as ScrollView2, StyleSheet as StyleSheet8, View as View7 } from "react-native";
491
+ import { Text as Text8 } from "react-native-paper";
492
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
498
493
  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 }) {
499
- const { dark, colors } = useAutoPaperTheme7();
494
+ const { dark, colors } = useAutoPaperTheme8();
500
495
  const ambientRotation = useRotation2();
501
496
  const rotation = rotationOverride ?? ambientRotation;
502
497
  const [confirmResetVisible, setConfirmResetVisible] = useState3(false);
503
498
  const showReset = !!onReset;
504
499
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
505
500
  const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
506
- return /* @__PURE__ */ jsxs8(View6, { style: [styles7.container, { backgroundColor: bg }], children: [
507
- /* @__PURE__ */ jsxs8(View6, { style: [styles7.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
508
- /* @__PURE__ */ jsx8(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
509
- /* @__PURE__ */ jsx8(Text7, { variant: titleVariant, style: [styles7.title, { color: fg }], children: title })
501
+ return /* @__PURE__ */ jsxs9(View7, { style: [styles8.container, { backgroundColor: bg }], children: [
502
+ /* @__PURE__ */ jsxs9(View7, { style: [styles8.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
503
+ /* @__PURE__ */ jsx9(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
504
+ /* @__PURE__ */ jsx9(Text8, { variant: titleVariant, style: [styles8.title, { color: fg }], children: title })
510
505
  ] }),
511
- /* @__PURE__ */ jsxs8(ScrollView2, { style: styles7.scrollView, contentContainerStyle: [styles7.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
506
+ /* @__PURE__ */ jsxs9(ScrollView2, { style: styles8.scrollView, contentContainerStyle: [styles8.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
512
507
  children,
513
- showReset && /* @__PURE__ */ jsx8(Button3, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles7.resetButton, children: resetLabel })
508
+ showReset && /* @__PURE__ */ jsx9(Button3, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles8.resetButton, children: resetLabel })
514
509
  ] }),
515
- /* @__PURE__ */ jsx8(
510
+ /* @__PURE__ */ jsx9(
516
511
  ConfirmDialog,
517
512
  {
518
513
  visible: confirmResetVisible,
@@ -532,7 +527,7 @@ function BaseStatsScreen({ rotation: rotationOverride, title = "Achievements", t
532
527
  )
533
528
  ] });
534
529
  }
535
- var styles7 = StyleSheet8.create({
530
+ var styles8 = StyleSheet8.create({
536
531
  container: {
537
532
  flex: 1
538
533
  },
@@ -563,19 +558,19 @@ var styles7 = StyleSheet8.create({
563
558
  });
564
559
 
565
560
  // src/ContentGutter.tsx
566
- import { computeContentBounds } from "@tastic/core";
567
- import { StyleSheet as StyleSheet9, useWindowDimensions, View as View7 } from "react-native";
568
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
561
+ import { computeContentBounds, useRotatedWindowDimensions } from "@tastic/core";
562
+ import { StyleSheet as StyleSheet9, View as View8 } from "react-native";
563
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
569
564
  function ContentGutter({ maxContentWidth, leftGutter, rightGutter, style, children }) {
570
- const { width: windowWidth } = useWindowDimensions();
565
+ const { width: windowWidth } = useRotatedWindowDimensions();
571
566
  const { contentWidth, gutterWidth } = computeContentBounds(windowWidth, maxContentWidth ?? Infinity);
572
- return /* @__PURE__ */ jsxs9(View7, { style: [styles8.row, style], children: [
573
- /* @__PURE__ */ jsx9(View7, { style: [styles8.gutter, { width: gutterWidth }], children: leftGutter }),
574
- /* @__PURE__ */ jsx9(View7, { style: { width: contentWidth }, children }),
575
- /* @__PURE__ */ jsx9(View7, { style: [styles8.gutter, { width: gutterWidth }], children: rightGutter })
567
+ return /* @__PURE__ */ jsxs10(View8, { style: [styles9.row, style], children: [
568
+ /* @__PURE__ */ jsx10(View8, { style: [styles9.gutter, { width: gutterWidth }], children: leftGutter }),
569
+ /* @__PURE__ */ jsx10(View8, { style: { width: contentWidth }, children }),
570
+ /* @__PURE__ */ jsx10(View8, { style: [styles9.gutter, { width: gutterWidth }], children: rightGutter })
576
571
  ] });
577
572
  }
578
- var styles8 = StyleSheet9.create({
573
+ var styles9 = StyleSheet9.create({
579
574
  // overflow:'hidden' keeps an oversized leftGutter/rightGutter from bleeding into the content
580
575
  // column instead of clipping cleanly at its own real width.
581
576
  gutter: {
@@ -594,34 +589,34 @@ import { Platform as Platform3 } from "react-native";
594
589
  import { getBlendedColor, getColorRoles } from "@rific/auto-paper";
595
590
  import { IconButton as IconButton2, TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
596
591
  import { useRotation as useRotation3 } from "@tastic/core";
597
- import { ScrollView as ScrollView3, StyleSheet as StyleSheet11, View as View10 } from "react-native";
598
- import { Icon as Icon4, Text as Text8 } from "react-native-paper";
592
+ import { ScrollView as ScrollView3, StyleSheet as StyleSheet11, View as View11 } from "react-native";
593
+ import { Icon as Icon5, Text as Text9 } from "react-native-paper";
599
594
 
600
595
  // src/PopoverBody.tsx
601
- import { StyleSheet as StyleSheet10, View as View8 } from "react-native";
602
- import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
596
+ import { StyleSheet as StyleSheet10, View as View9 } from "react-native";
597
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
603
598
  var CARET_SIZE = 7;
604
599
  var CARET_RING_OFFSET = 2;
605
600
  var CARET_DIP = 1;
606
601
  function PopoverBody({ visible, children, align = "center", verticalAlign = "below", caretColor, caretBorderColor, caretBorderWidth = 1, triggerSize = 0 }) {
607
602
  if (!visible) return null;
608
603
  const above = verticalAlign === "above";
609
- const alignStyle = align === "left" ? styles9.contentLeft : align === "right" ? styles9.contentRight : styles9.contentCenter;
610
- const verticalStyle = above ? styles9.contentAbove : styles9.contentBelow;
604
+ const alignStyle = align === "left" ? styles10.contentLeft : align === "right" ? styles10.contentRight : styles10.contentCenter;
605
+ const verticalStyle = above ? styles10.contentAbove : styles10.contentBelow;
611
606
  const ringSize = CARET_SIZE + caretBorderWidth;
612
607
  const caretOffset = (halfFootprint) => align === "right" ? { right: triggerSize / 2 - halfFootprint } : { left: triggerSize / 2 - halfFootprint };
613
608
  const caretFill = above ? "borderTopColor" : "borderBottomColor";
614
609
  const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
615
610
  const caretEdge = (size) => above ? { bottom: -size + CARET_DIP } : { top: -size + CARET_DIP };
616
- return /* @__PURE__ */ jsxs10(View8, { style: [styles9.content, alignStyle, verticalStyle], children: [
611
+ return /* @__PURE__ */ jsxs11(View9, { style: [styles10.content, alignStyle, verticalStyle], children: [
617
612
  children,
618
- caretColor && /* @__PURE__ */ jsxs10(Fragment3, { children: [
619
- /* @__PURE__ */ jsx10(View8, { style: [styles9.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
620
- /* @__PURE__ */ jsx10(View8, { style: [styles9.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
613
+ caretColor && /* @__PURE__ */ jsxs11(Fragment3, { children: [
614
+ /* @__PURE__ */ jsx11(View9, { style: [styles10.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
615
+ /* @__PURE__ */ jsx11(View9, { style: [styles10.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
621
616
  ] })
622
617
  ] });
623
618
  }
624
- var styles9 = StyleSheet10.create({
619
+ var styles10 = StyleSheet10.create({
625
620
  caret: {
626
621
  borderLeftColor: "transparent",
627
622
  borderRightColor: "transparent",
@@ -664,15 +659,15 @@ var styles9 = StyleSheet10.create({
664
659
  });
665
660
 
666
661
  // src/TriggerGaugeHost.tsx
667
- import { jsx as jsx11 } from "react/jsx-runtime";
662
+ import { jsx as jsx12 } from "react/jsx-runtime";
668
663
  var getTriggerGauge = () => import("./TriggerGauge-2OJOOF6I.mjs").then((mod) => ({ default: mod.TriggerGauge }));
669
664
  function TriggerGaugeHost(props) {
670
- return /* @__PURE__ */ jsx11(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
665
+ return /* @__PURE__ */ jsx12(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
671
666
  }
672
667
 
673
668
  // src/useAutoAlign.ts
674
669
  import { useCallback as useCallback2, useEffect, useRef as useRef2, useState as useState4 } from "react";
675
- import { useWindowDimensions as useWindowDimensions2 } from "react-native";
670
+ import { useWindowDimensions } from "react-native";
676
671
  var EDGE_MARGIN = 12;
677
672
  function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
678
673
  if (rotation === 0) return { x, y, width, height };
@@ -685,7 +680,7 @@ function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
685
680
  }
686
681
  function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
687
682
  const triggerRef = useRef2(null);
688
- const { width: windowWidth, height: windowHeight } = useWindowDimensions2();
683
+ const { width: windowWidth, height: windowHeight } = useWindowDimensions();
689
684
  const [align, setAlign] = useState4("center");
690
685
  const [verticalAlign, setVerticalAlign] = useState4("below");
691
686
  const [maxHeight, setMaxHeight] = useState4(contentHeight);
@@ -719,7 +714,7 @@ function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
719
714
  }
720
715
 
721
716
  // src/SectionedDropdown.tsx
722
- import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
717
+ import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
723
718
  var MENU_BORDER_WIDTH = 1;
724
719
  var TRIGGER_SIZE = 44;
725
720
  var MENU_MIN_WIDTH = 200;
@@ -769,17 +764,17 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
769
764
  }
770
765
  gaugeOffset += section.options.length;
771
766
  }
772
- const anchorStyle = [styles10.anchor, open && styles10.anchorOpen];
767
+ const anchorStyle = [styles11.anchor, open && styles11.anchorOpen];
773
768
  const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
774
769
  const dividerStyle = { backgroundColor: menuBorderColor };
775
- return /* @__PURE__ */ jsxs11(View10, { style: anchorStyle, children: [
776
- /* @__PURE__ */ jsxs11(View10, { ref: triggerRef, collapsable: false, style: styles10.triggerBox, children: [
777
- /* @__PURE__ */ jsx12(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
778
- /* @__PURE__ */ jsx12(IconButton2, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
770
+ return /* @__PURE__ */ jsxs12(View11, { style: anchorStyle, children: [
771
+ /* @__PURE__ */ jsxs12(View11, { ref: triggerRef, collapsable: false, style: styles11.triggerBox, children: [
772
+ /* @__PURE__ */ jsx13(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
773
+ /* @__PURE__ */ jsx13(IconButton2, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
779
774
  ] }),
780
- /* @__PURE__ */ jsx12(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx12(ScrollView3, { style: [styles10.menu, menuStyle], contentContainerStyle: styles10.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs11(View10, { children: [
781
- sectionIndex > 0 && /* @__PURE__ */ jsx12(View10, { style: [styles10.divider, dividerStyle] }),
782
- section.kind === "single" ? /* @__PURE__ */ jsx12(
775
+ /* @__PURE__ */ jsx13(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx13(ScrollView3, { style: [styles11.menu, menuStyle], contentContainerStyle: styles11.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs12(View11, { children: [
776
+ sectionIndex > 0 && /* @__PURE__ */ jsx13(View11, { style: [styles11.divider, dividerStyle] }),
777
+ section.kind === "single" ? /* @__PURE__ */ jsx13(
783
778
  SingleSection,
784
779
  {
785
780
  section,
@@ -792,7 +787,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
792
787
  if (autoDismiss) host.close();
793
788
  }
794
789
  }
795
- ) : /* @__PURE__ */ jsx12(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
790
+ ) : /* @__PURE__ */ jsx13(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
796
791
  ] }, section.id)) }) })
797
792
  ] });
798
793
  }
@@ -804,18 +799,18 @@ function SingleSection({
804
799
  labelFontFamily,
805
800
  onSelect
806
801
  }) {
807
- return /* @__PURE__ */ jsx12(Fragment4, { children: section.options.map((option) => {
802
+ return /* @__PURE__ */ jsx13(Fragment4, { children: section.options.map((option) => {
808
803
  const selected = option.value === section.value;
809
804
  const taken = !selected && option.value === section.takenValue;
810
805
  const rowColor = selected ? onAccentColor : mutedColor;
811
- const itemStyle = [styles10.item, selected && { backgroundColor: accentColor }, taken && styles10.itemTaken];
806
+ const itemStyle = [styles11.item, selected && { backgroundColor: accentColor }, taken && styles11.itemTaken];
812
807
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
813
808
  const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
814
- return /* @__PURE__ */ jsx12(TouchableRipple2, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs11(View10, { style: styles10.itemRow, children: [
815
- option.icon && /* @__PURE__ */ jsx12(Icon4, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
816
- /* @__PURE__ */ jsxs11(View10, { style: styles10.itemLabelColumn, children: [
817
- /* @__PURE__ */ jsx12(Text8, { variant: "labelLarge", style: labelStyle, children: option.label }),
818
- option.description && /* @__PURE__ */ jsx12(Text8, { variant: "labelSmall", style: descriptionStyle, children: option.description })
809
+ return /* @__PURE__ */ jsx13(TouchableRipple2, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs12(View11, { style: styles11.itemRow, children: [
810
+ option.icon && /* @__PURE__ */ jsx13(Icon5, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
811
+ /* @__PURE__ */ jsxs12(View11, { style: styles11.itemLabelColumn, children: [
812
+ /* @__PURE__ */ jsx13(Text9, { variant: "labelLarge", style: labelStyle, children: option.label }),
813
+ option.description && /* @__PURE__ */ jsx13(Text9, { variant: "labelSmall", style: descriptionStyle, children: option.description })
819
814
  ] })
820
815
  ] }) }, option.value);
821
816
  }) });
@@ -829,23 +824,23 @@ function MultiSection({
829
824
  allClearLabels
830
825
  }) {
831
826
  const allSelected = section.value.length === section.options.length;
832
- return /* @__PURE__ */ jsxs11(Fragment4, { children: [
827
+ return /* @__PURE__ */ jsxs12(Fragment4, { children: [
833
828
  section.options.map((option, i) => {
834
829
  const selected = section.value.includes(option.value);
835
830
  const rowColor = selected ? onAccentColor : mutedColor;
836
831
  const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
837
832
  const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
838
- const itemStyle = [styles10.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
833
+ const itemStyle = [styles11.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
839
834
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
840
- return /* @__PURE__ */ jsx12(TouchableRipple2, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs11(View10, { style: styles10.itemRow, children: [
841
- option.icon && /* @__PURE__ */ jsx12(Icon4, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
842
- /* @__PURE__ */ jsx12(Text8, { variant: "labelLarge", style: [styles10.itemLabelFlex, labelStyle], children: option.label })
835
+ return /* @__PURE__ */ jsx13(TouchableRipple2, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs12(View11, { style: styles11.itemRow, children: [
836
+ option.icon && /* @__PURE__ */ jsx13(Icon5, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
837
+ /* @__PURE__ */ jsx13(Text9, { variant: "labelLarge", style: [styles11.itemLabelFlex, labelStyle], children: option.label })
843
838
  ] }) }, option.value);
844
839
  }),
845
- section.allClear && /* @__PURE__ */ jsx12(TouchableRipple2, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles10.item, children: /* @__PURE__ */ jsx12(Text8, { variant: "labelLarge", style: [styles10.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
840
+ section.allClear && /* @__PURE__ */ jsx13(TouchableRipple2, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles11.item, children: /* @__PURE__ */ jsx13(Text9, { variant: "labelLarge", style: [styles11.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
846
841
  ] });
847
842
  }
848
- var styles10 = StyleSheet11.create({
843
+ var styles11 = StyleSheet11.create({
849
844
  allClearLabel: {
850
845
  fontWeight: "bold",
851
846
  textAlign: "center"
@@ -915,24 +910,24 @@ var styles10 = StyleSheet11.create({
915
910
  });
916
911
 
917
912
  // src/ControlSchemePicker.tsx
918
- import { jsx as jsx13 } from "react/jsx-runtime";
913
+ import { jsx as jsx14 } from "react/jsx-runtime";
919
914
  function ControlSchemePicker({ id, host, value, onChange, options, takenValue, accentColor, mutedColor, onAccentColor, dark, align, alignOverride, rotation, labelFontFamily }) {
920
915
  const isTouchPrimary = useIsTouchPrimaryDevice2();
921
916
  if (Platform3.OS !== "web" || isTouchPrimary) return null;
922
- return /* @__PURE__ */ jsx13(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 });
917
+ return /* @__PURE__ */ jsx14(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 });
923
918
  }
924
919
 
925
920
  // src/CornerActionButtons.tsx
926
921
  import { IconButton as IconButton3 } from "@rific/feedback-press";
927
922
  import { StyleSheet as StyleSheet12 } from "react-native";
928
- import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
923
+ import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
929
924
  function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon = "arrow-left", backLabel = "Back" }) {
930
- return /* @__PURE__ */ jsxs12(Fragment5, { children: [
931
- /* @__PURE__ */ jsx14(IconButton3, { icon: backIcon, iconColor: fg, size: 24, style: [styles11.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: backLabel }),
932
- /* @__PURE__ */ jsx14(IconButton3, { icon: "cog", iconColor: fg, size: 24, style: [styles11.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
925
+ return /* @__PURE__ */ jsxs13(Fragment5, { children: [
926
+ /* @__PURE__ */ jsx15(IconButton3, { icon: backIcon, iconColor: fg, size: 24, style: [styles12.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: backLabel }),
927
+ /* @__PURE__ */ jsx15(IconButton3, { icon: "cog", iconColor: fg, size: 24, style: [styles12.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
933
928
  ] });
934
929
  }
935
- var styles11 = StyleSheet12.create({
930
+ var styles12 = StyleSheet12.create({
936
931
  back: {
937
932
  position: "absolute"
938
933
  },
@@ -942,22 +937,22 @@ var styles11 = StyleSheet12.create({
942
937
  });
943
938
 
944
939
  // src/CornerStatusBadges.tsx
945
- import { StyleSheet as StyleSheet13, View as View11 } from "react-native";
946
- import { Icon as Icon5 } from "react-native-paper";
947
- import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
940
+ import { StyleSheet as StyleSheet13, View as View12 } from "react-native";
941
+ import { Icon as Icon6 } from "react-native-paper";
942
+ import { Fragment as Fragment6, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
948
943
  var BADGE_SIZE = 30;
949
944
  function StatusBadge({ icon }) {
950
- return /* @__PURE__ */ jsx15(View11, { style: styles12.badge, children: icon && /* @__PURE__ */ jsx15(Icon5, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
945
+ return /* @__PURE__ */ jsx16(View12, { style: styles13.badge, children: icon && /* @__PURE__ */ jsx16(Icon6, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
951
946
  }
952
947
  function CornerStatusBadges({ orientationMode, p1OnRight, p1, p2 }) {
953
948
  const p1OnRightSide = orientationMode === "sideBySide" && p1OnRight;
954
949
  const p2OnLeftSide = orientationMode === "sideBySide" && !p1OnRight;
955
- return /* @__PURE__ */ jsxs13(Fragment6, { children: [
956
- p1 && /* @__PURE__ */ jsx15(View11, { pointerEvents: "none", style: p1OnRightSide ? styles12.bottomRight : styles12.bottomLeft, children: /* @__PURE__ */ jsx15(StatusBadge, { icon: p1.icon }) }),
957
- p2 && /* @__PURE__ */ jsx15(View11, { pointerEvents: "none", style: p2OnLeftSide ? styles12.topLeft : styles12.topRight, children: /* @__PURE__ */ jsx15(StatusBadge, { icon: p2.icon }) })
950
+ return /* @__PURE__ */ jsxs14(Fragment6, { children: [
951
+ p1 && /* @__PURE__ */ jsx16(View12, { pointerEvents: "none", style: p1OnRightSide ? styles13.bottomRight : styles13.bottomLeft, children: /* @__PURE__ */ jsx16(StatusBadge, { icon: p1.icon }) }),
952
+ p2 && /* @__PURE__ */ jsx16(View12, { pointerEvents: "none", style: p2OnLeftSide ? styles13.topLeft : styles13.topRight, children: /* @__PURE__ */ jsx16(StatusBadge, { icon: p2.icon }) })
958
953
  ] });
959
954
  }
960
- var styles12 = StyleSheet13.create({
955
+ var styles13 = StyleSheet13.create({
961
956
  badge: {
962
957
  alignItems: "center",
963
958
  backgroundColor: "rgba(0,0,0,0.28)",
@@ -976,12 +971,12 @@ var styles12 = StyleSheet13.create({
976
971
  });
977
972
 
978
973
  // src/InlineColorPicker.tsx
979
- import { defaultColors, getContrastColor } from "@rific/auto-paper";
974
+ import { defaultColors, getContrastColor as getContrastColor2 } from "@rific/auto-paper";
980
975
  import { TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
981
976
  import { clamp, useRotation as useRotation4 } from "@tastic/core";
982
- import { ScrollView as ScrollView4, StyleSheet as StyleSheet14, useWindowDimensions as useWindowDimensions3, View as View12 } from "react-native";
983
- import { Icon as Icon6, Text as Text9 } from "react-native-paper";
984
- import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
977
+ import { ScrollView as ScrollView4, StyleSheet as StyleSheet14, useWindowDimensions as useWindowDimensions2, View as View13 } from "react-native";
978
+ import { Icon as Icon7, Text as Text10 } from "react-native-paper";
979
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
985
980
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
986
981
  var DEFAULT_SIZE = 48;
987
982
  var SWATCH_SIZE = 28;
@@ -1006,7 +1001,7 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1006
1001
  const ambientRotation = useRotation4();
1007
1002
  const rotation = rotationOverride ?? ambientRotation;
1008
1003
  const menuBg = dark ? "#000000" : "#FFFFFF";
1009
- const { width: windowWidth } = useWindowDimensions3();
1004
+ const { width: windowWidth } = useWindowDimensions2();
1010
1005
  const { width: swatchesWidth, height: swatchesHeight } = getInlineColorPickerContentSize(swatches.length, windowWidth, columns);
1011
1006
  const open = host.openId === id;
1012
1007
  const auto = useAutoAlign(open, swatchesWidth, swatchesHeight, rotation);
@@ -1015,16 +1010,16 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1015
1010
  const align = forcedAlign ?? placement.align;
1016
1011
  const displayFor = previewValue ?? ((hex) => hex);
1017
1012
  const displayValue = displayFor(value);
1018
- const contrastColor = getContrastColor(displayValue);
1013
+ const contrastColor = getContrastColor2(displayValue);
1019
1014
  const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
1020
- return /* @__PURE__ */ jsxs14(View12, { style: [styles13.anchor, open && styles13.anchorOpen], children: [
1021
- /* @__PURE__ */ jsx16(TouchableRipple3, { onPress: () => host.toggle(id), borderless: true, style: [styles13.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx16(View12, { ref: triggerRef, collapsable: false, style: styles13.triggerMeasure, children: tag ? /* @__PURE__ */ jsx16(Text9, { style: [styles13.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx16(Icon6, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
1022
- /* @__PURE__ */ jsx16(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx16(ScrollView4, { style: [styles13.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles13.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
1015
+ return /* @__PURE__ */ jsxs15(View13, { style: [styles14.anchor, open && styles14.anchorOpen], children: [
1016
+ /* @__PURE__ */ jsx17(TouchableRipple3, { onPress: () => host.toggle(id), borderless: true, style: [styles14.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx17(View13, { ref: triggerRef, collapsable: false, style: styles14.triggerMeasure, children: tag ? /* @__PURE__ */ jsx17(Text10, { style: [styles14.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx17(Icon7, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
1017
+ /* @__PURE__ */ jsx17(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx17(ScrollView4, { style: [styles14.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles14.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
1023
1018
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
1024
1019
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
1025
1020
  const swappable = taken && allowSwapTaken;
1026
1021
  const swatchDisplay = displayFor(swatch.value);
1027
- return /* @__PURE__ */ jsx16(
1022
+ return /* @__PURE__ */ jsx17(
1028
1023
  TouchableRipple3,
1029
1024
  {
1030
1025
  disabled: taken && !swappable,
@@ -1033,15 +1028,15 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
1033
1028
  if (autoDismiss) host.close();
1034
1029
  },
1035
1030
  borderless: true,
1036
- style: [styles13.swatch, { backgroundColor: swatchDisplay }, selected && styles13.swatchSelected, taken && !swappable && styles13.swatchTaken],
1037
- children: selected ? /* @__PURE__ */ jsx16(Icon6, { source: "check", size: 16, color: getContrastColor(swatchDisplay) }) : swappable ? /* @__PURE__ */ jsx16(Icon6, { source: "swap-horizontal", size: 16, color: getContrastColor(swatchDisplay) }) : taken ? /* @__PURE__ */ jsx16(Icon6, { source: "close", size: 16, color: getContrastColor(swatchDisplay) }) : /* @__PURE__ */ jsx16(View12, {})
1031
+ style: [styles14.swatch, { backgroundColor: swatchDisplay }, selected && styles14.swatchSelected, taken && !swappable && styles14.swatchTaken],
1032
+ children: selected ? /* @__PURE__ */ jsx17(Icon7, { source: "check", size: 16, color: getContrastColor2(swatchDisplay) }) : swappable ? /* @__PURE__ */ jsx17(Icon7, { source: "swap-horizontal", size: 16, color: getContrastColor2(swatchDisplay) }) : taken ? /* @__PURE__ */ jsx17(Icon7, { source: "close", size: 16, color: getContrastColor2(swatchDisplay) }) : /* @__PURE__ */ jsx17(View13, {})
1038
1033
  },
1039
1034
  swatch.value
1040
1035
  );
1041
1036
  }) }) })
1042
1037
  ] });
1043
1038
  }
1044
- var styles13 = StyleSheet14.create({
1039
+ var styles14 = StyleSheet14.create({
1045
1040
  anchor: {
1046
1041
  position: "relative"
1047
1042
  },
@@ -1113,11 +1108,11 @@ var styles13 = StyleSheet14.create({
1113
1108
  });
1114
1109
 
1115
1110
  // src/LabeledDropdown.tsx
1116
- import { getContrastColor as getContrastColor2 } from "@rific/auto-paper";
1111
+ import { getContrastColor as getContrastColor3 } from "@rific/auto-paper";
1117
1112
  import { TouchableRipple as TouchableRipple4 } from "@rific/feedback-press";
1118
- import { ScrollView as ScrollView5, StyleSheet as StyleSheet15, View as View13 } from "react-native";
1119
- import { Icon as Icon7, Text as Text10 } from "react-native-paper";
1120
- import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1113
+ import { ScrollView as ScrollView5, StyleSheet as StyleSheet15, View as View14 } from "react-native";
1114
+ import { Icon as Icon8, Text as Text11 } from "react-native-paper";
1115
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
1121
1116
  var TRIGGER_HEIGHT = 28;
1122
1117
  var POPOVER_WIDTH = 180;
1123
1118
  var ROW_HEIGHT = 36;
@@ -1140,22 +1135,22 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
1140
1135
  onChange(option.value);
1141
1136
  host.close();
1142
1137
  };
1143
- return /* @__PURE__ */ jsxs15(View13, { style: [styles14.anchor, open && styles14.anchorOpen], children: [
1144
- /* @__PURE__ */ jsx17(TouchableRipple4, { onPress: () => host.toggle(id), style: styles14.trigger, children: /* @__PURE__ */ jsxs15(View13, { ref: triggerRef, collapsable: false, style: styles14.triggerInner, children: [
1145
- /* @__PURE__ */ jsx17(Text10, { style: [styles14.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
1146
- /* @__PURE__ */ jsx17(Icon7, { source: "menu-down", size: 16, color })
1138
+ return /* @__PURE__ */ jsxs16(View14, { style: [styles15.anchor, open && styles15.anchorOpen], children: [
1139
+ /* @__PURE__ */ jsx18(TouchableRipple4, { onPress: () => host.toggle(id), style: styles15.trigger, children: /* @__PURE__ */ jsxs16(View14, { ref: triggerRef, collapsable: false, style: styles15.triggerInner, children: [
1140
+ /* @__PURE__ */ jsx18(Text11, { style: [styles15.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
1141
+ /* @__PURE__ */ jsx18(Icon8, { source: "menu-down", size: 16, color })
1147
1142
  ] }) }),
1148
- /* @__PURE__ */ jsx17(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ jsx17(ScrollView5, { style: [styles14.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles14.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
1143
+ /* @__PURE__ */ jsx18(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ jsx18(ScrollView5, { style: [styles15.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles15.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
1149
1144
  const isSelected = option.value === value;
1150
- const onColor = getContrastColor2(color);
1151
- return /* @__PURE__ */ jsx17(TouchableRipple4, { onPress: () => handleSelect(option), style: [styles14.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs15(View13, { style: styles14.rowInner, children: [
1152
- option.icon && /* @__PURE__ */ jsx17(Icon7, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
1153
- /* @__PURE__ */ jsx17(Text10, { style: [styles14.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
1145
+ const onColor = getContrastColor3(color);
1146
+ return /* @__PURE__ */ jsx18(TouchableRipple4, { onPress: () => handleSelect(option), style: [styles15.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs16(View14, { style: styles15.rowInner, children: [
1147
+ option.icon && /* @__PURE__ */ jsx18(Icon8, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
1148
+ /* @__PURE__ */ jsx18(Text11, { style: [styles15.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
1154
1149
  ] }) }, option.value);
1155
1150
  }) }) })
1156
1151
  ] });
1157
1152
  }
1158
- var styles14 = StyleSheet15.create({
1153
+ var styles15 = StyleSheet15.create({
1159
1154
  anchor: {
1160
1155
  position: "relative"
1161
1156
  },
@@ -1214,21 +1209,21 @@ function loadSkiaWeb() {
1214
1209
 
1215
1210
  // src/PlayerSetupPanel.tsx
1216
1211
  import { ProfilePicker } from "@tastic/profile";
1217
- import { StyleSheet as StyleSheet17, View as View14 } from "react-native";
1218
- import { Text as Text12 } from "react-native-paper";
1212
+ import { StyleSheet as StyleSheet17, View as View15 } from "react-native";
1213
+ import { Text as Text13 } from "react-native-paper";
1219
1214
 
1220
1215
  // src/ReadyButton.tsx
1221
1216
  import { TouchableRipple as TouchableRipple5 } from "@rific/feedback-press";
1222
1217
  import { StyleSheet as StyleSheet16 } from "react-native";
1223
- import { Text as Text11 } from "react-native-paper";
1224
- import { jsx as jsx18 } from "react/jsx-runtime";
1218
+ import { Text as Text12 } from "react-native-paper";
1219
+ import { jsx as jsx19 } from "react/jsx-runtime";
1225
1220
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
1226
- const readyButtonStyle = [styles15.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
1221
+ const readyButtonStyle = [styles16.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
1227
1222
  const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
1228
1223
  const labelColorStyle = { color: ready ? "#000000" : color };
1229
- return /* @__PURE__ */ jsx18(TouchableRipple5, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx18(Text11, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
1224
+ return /* @__PURE__ */ jsx19(TouchableRipple5, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx19(Text12, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
1230
1225
  }
1231
- var styles15 = StyleSheet16.create({
1226
+ var styles16 = StyleSheet16.create({
1232
1227
  readyButton: {
1233
1228
  alignItems: "center",
1234
1229
  borderRadius: 12,
@@ -1252,7 +1247,7 @@ function usePopoverHost() {
1252
1247
  }
1253
1248
 
1254
1249
  // src/PlayerSetupPanel.tsx
1255
- import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
1250
+ import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
1256
1251
  function getColorPopoverId(idPrefix) {
1257
1252
  return `${idPrefix}-color`;
1258
1253
  }
@@ -1267,16 +1262,16 @@ function PlayerSetupPanel({ idPrefix, host, label, labelFontFamily, color, onCol
1267
1262
  const showProfilePicker = isHuman && profiles !== void 0 && !!onProfileSelect;
1268
1263
  const showCpuDifficulty = !isHuman && cpuDifficulty !== void 0 && cpuDifficultyOptions !== void 0 && !!onCpuDifficultyChange;
1269
1264
  const selectedProfile = profiles?.find((p) => p.id === selectedProfileId) ?? null;
1270
- return /* @__PURE__ */ jsxs16(View14, { style: [styles16.panel, ownPopoverOpen && styles16.panelOpen], children: [
1271
- showProfilePicker ? /* @__PURE__ */ jsx19(ProfilePicker, { idPrefix, host: popover, profiles, selectedId: selectedProfileId ?? null, takenId: takenProfileId, color, dark, guestLabel: guestLabel ?? "GUEST", onSelect: (profile) => onProfileSelect?.(profile), onManage: onManageProfiles }) : showCpuDifficulty ? /* @__PURE__ */ jsx19(LabeledDropdown, { id: getCpuDifficultyPopoverId(idPrefix), host: popover, options: cpuDifficultyOptions, value: cpuDifficulty, onChange: onCpuDifficultyChange, color, dark, alignOverride: cpuDifficultyAlignOverride }) : label && /* @__PURE__ */ jsx19(Text12, { variant: "labelSmall", style: { color, fontFamily: labelFontFamily }, children: label }),
1272
- /* @__PURE__ */ jsxs16(View14, { style: [styles16.pickerRow, pickerRowPopoverOpen && styles16.pickerRowOpen], children: [
1273
- /* @__PURE__ */ jsx19(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 }),
1265
+ return /* @__PURE__ */ jsxs17(View15, { style: [styles17.panel, ownPopoverOpen && styles17.panelOpen], children: [
1266
+ showProfilePicker ? /* @__PURE__ */ jsx20(ProfilePicker, { idPrefix, host: popover, profiles, selectedId: selectedProfileId ?? null, takenId: takenProfileId, color, dark, guestLabel: guestLabel ?? "GUEST", onSelect: (profile) => onProfileSelect?.(profile), onManage: onManageProfiles }) : showCpuDifficulty ? /* @__PURE__ */ jsx20(LabeledDropdown, { id: getCpuDifficultyPopoverId(idPrefix), host: popover, options: cpuDifficultyOptions, value: cpuDifficulty, onChange: onCpuDifficultyChange, color, dark, alignOverride: cpuDifficultyAlignOverride }) : label && /* @__PURE__ */ jsx20(Text13, { variant: "labelSmall", style: { color, fontFamily: labelFontFamily }, children: label }),
1267
+ /* @__PURE__ */ jsxs17(View15, { style: [styles17.pickerRow, pickerRowPopoverOpen && styles17.pickerRowOpen], children: [
1268
+ /* @__PURE__ */ jsx20(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 }),
1274
1269
  secondPicker
1275
1270
  ] }),
1276
- isHuman && showReadyButton && onToggleReady && /* @__PURE__ */ jsx19(ReadyButton, { color, ready: ready ?? false, onToggleReady })
1271
+ isHuman && showReadyButton && onToggleReady && /* @__PURE__ */ jsx20(ReadyButton, { color, ready: ready ?? false, onToggleReady })
1277
1272
  ] });
1278
1273
  }
1279
- var styles16 = StyleSheet17.create({
1274
+ var styles17 = StyleSheet17.create({
1280
1275
  panel: {
1281
1276
  alignItems: "center",
1282
1277
  gap: 12
@@ -1309,28 +1304,28 @@ var styles16 = StyleSheet17.create({
1309
1304
 
1310
1305
  // src/PressAwayOverlay.tsx
1311
1306
  import { Pressable, StyleSheet as StyleSheet18 } from "react-native";
1312
- import { jsx as jsx20 } from "react/jsx-runtime";
1307
+ import { jsx as jsx21 } from "react/jsx-runtime";
1313
1308
  function PressAwayOverlay({ active, onPress, style }) {
1314
1309
  if (!active) return null;
1315
- return /* @__PURE__ */ jsx20(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet18.absoluteFill, style] });
1310
+ return /* @__PURE__ */ jsx21(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet18.absoluteFill, style] });
1316
1311
  }
1317
1312
 
1318
1313
  // src/SharedActionBand.tsx
1319
1314
  import { IconButton as IconButton4 } from "@rific/feedback-press";
1320
- import { StyleSheet as StyleSheet19, View as View15 } from "react-native";
1321
- import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
1315
+ import { StyleSheet as StyleSheet19, View as View16 } from "react-native";
1316
+ import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
1322
1317
  function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
1323
- return /* @__PURE__ */ jsxs17(View15, { style: [styles17.column, popoverOpen && styles17.columnOpen], children: [
1324
- /* @__PURE__ */ jsxs17(View15, { style: styles17.actionsRow, children: [
1325
- /* @__PURE__ */ jsx21(IconButton4, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1326
- onRandomize && /* @__PURE__ */ jsx21(IconButton4, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1327
- onReset && /* @__PURE__ */ jsx21(IconButton4, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
1328
- /* @__PURE__ */ jsx21(IconButton4, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
1318
+ return /* @__PURE__ */ jsxs18(View16, { style: [styles18.column, popoverOpen && styles18.columnOpen], children: [
1319
+ /* @__PURE__ */ jsxs18(View16, { style: styles18.actionsRow, children: [
1320
+ /* @__PURE__ */ jsx22(IconButton4, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
1321
+ onRandomize && /* @__PURE__ */ jsx22(IconButton4, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
1322
+ onReset && /* @__PURE__ */ jsx22(IconButton4, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
1323
+ /* @__PURE__ */ jsx22(IconButton4, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
1329
1324
  ] }),
1330
1325
  children
1331
1326
  ] });
1332
1327
  }
1333
- var styles17 = StyleSheet19.create({
1328
+ var styles18 = StyleSheet19.create({
1334
1329
  actionsRow: {
1335
1330
  alignItems: "center",
1336
1331
  flexDirection: "row",
@@ -1347,9 +1342,9 @@ var styles17 = StyleSheet19.create({
1347
1342
 
1348
1343
  // src/StaggeredWord.tsx
1349
1344
  import { useEffect as useEffect2 } from "react";
1350
- import { StyleSheet as StyleSheet20, View as View16 } from "react-native";
1345
+ import { StyleSheet as StyleSheet20, View as View17 } from "react-native";
1351
1346
  import Animated, { Easing, useAnimatedStyle, useSharedValue, withDelay, withTiming } from "react-native-reanimated";
1352
- import { jsx as jsx22 } from "react/jsx-runtime";
1347
+ import { jsx as jsx23 } from "react/jsx-runtime";
1353
1348
  var STAGGER_MS = 45;
1354
1349
  var LETTER_DURATION_MS = 320;
1355
1350
  function getStaggeredWordDuration(letterCount) {
@@ -1365,12 +1360,12 @@ function AnimatedLetter({ char, index, color, reducedMotion, fontFamily, fontSiz
1365
1360
  opacity: progress.value,
1366
1361
  transform: [{ translateY: (1 - progress.value) * 12 }, { scale: 0.85 + progress.value * 0.15 }]
1367
1362
  }));
1368
- return /* @__PURE__ */ jsx22(Animated.Text, { allowFontScaling: false, style: [{ color, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, style], children: char });
1363
+ return /* @__PURE__ */ jsx23(Animated.Text, { allowFontScaling: false, style: [{ color, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, style], children: char });
1369
1364
  }
1370
1365
  function StaggeredWord({ word, color, fontFamily, fontSize, lineHeight, reducedMotion, startIndex = 0, fontWeight = "bold", letterSpacing, style, onLayout }) {
1371
- return /* @__PURE__ */ jsx22(View16, { style: [styles18.row, style], onLayout, children: word.split("").map((char, i) => /* @__PURE__ */ jsx22(AnimatedLetter, { char, index: startIndex + i, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, i)) });
1366
+ return /* @__PURE__ */ jsx23(View17, { style: [styles19.row, style], onLayout, children: word.split("").map((char, i) => /* @__PURE__ */ jsx23(AnimatedLetter, { char, index: startIndex + i, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, i)) });
1372
1367
  }
1373
- var styles18 = StyleSheet20.create({
1368
+ var styles19 = StyleSheet20.create({
1374
1369
  row: {
1375
1370
  flexDirection: "row"
1376
1371
  }
@@ -1378,12 +1373,12 @@ var styles18 = StyleSheet20.create({
1378
1373
 
1379
1374
  // src/UpdateDialog.tsx
1380
1375
  import { useUpdater as useUpdater2 } from "@rific/updater";
1381
- import { jsx as jsx23 } from "react/jsx-runtime";
1376
+ import { jsx as jsx24 } from "react/jsx-runtime";
1382
1377
  function UpdateDialog({ rotation = 0, autoCheck = true, autoPrompt = true, onError }) {
1383
1378
  const { manifest, onConfirm, respond } = useUpdateConfirmBridge();
1384
1379
  useUpdater2({ autoCheck, autoPrompt, onConfirm, onError });
1385
1380
  const date = manifest ? new Date(manifest.createdAt) : null;
1386
- return /* @__PURE__ */ jsx23(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) });
1381
+ return /* @__PURE__ */ jsx24(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) });
1387
1382
  }
1388
1383
 
1389
1384
  // src/useQuitConfirmation.ts
@@ -1409,13 +1404,13 @@ function useQuitConfirmation(hasProgress, onConfirmedBack) {
1409
1404
  // src/useZoneClampedAlign.ts
1410
1405
  import { useZoneBounds } from "@tastic/split-screen";
1411
1406
  import { useEffect as useEffect4, useState as useState7 } from "react";
1412
- import { useWindowDimensions as useWindowDimensions4 } from "react-native";
1407
+ import { useWindowDimensions as useWindowDimensions3 } from "react-native";
1413
1408
  import { useSafeAreaInsets } from "react-native-safe-area-context";
1414
1409
  var EDGE_MARGIN2 = 12;
1415
1410
  function useZoneClampedAlign(open, contentWidth, contentHeight, rotation = 0) {
1416
1411
  const auto = useAutoAlign(open, contentWidth, contentHeight, rotation);
1417
1412
  const zone = useZoneBounds();
1418
- const { height: windowHeight } = useWindowDimensions4();
1413
+ const { height: windowHeight } = useWindowDimensions3();
1419
1414
  const insets = useSafeAreaInsets();
1420
1415
  const shouldMeasure = open && auto.measured && zone !== null;
1421
1416
  const [ownBounds, setOwnBounds] = useState7(null);
@@ -1447,6 +1442,7 @@ function useZoneClampedAlign(open, contentWidth, contentHeight, rotation = 0) {
1447
1442
  export {
1448
1443
  AchievementCatalogSection,
1449
1444
  AchievementRow,
1445
+ AchievementUnlockList,
1450
1446
  ActivityStatSection,
1451
1447
  BaseSettingsDialog,
1452
1448
  BaseStatsScreen,
@@ -1479,6 +1475,7 @@ export {
1479
1475
  getOverlayCardColors,
1480
1476
  getStaggeredWordDuration,
1481
1477
  loadSkiaWeb,
1478
+ overlayActionStyles,
1482
1479
  overlayStyles,
1483
1480
  useAutoAlign,
1484
1481
  usePopoverHost,