@tastic/shell 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -64,7 +64,7 @@ __export(index_exports, {
64
64
  profileSelectionActions: () => profileSelectionActions,
65
65
  profileSelectionReducer: () => profileSelectionSlice_default,
66
66
  roundModeFor: () => roundModeFor,
67
- safeBack: () => import_core8.safeBack,
67
+ safeBack: () => import_core7.safeBack,
68
68
  seatColorsActions: () => seatColorsActions,
69
69
  seatPlayerKey: () => seatPlayerKey,
70
70
  seatTwoFor: () => seatTwoFor,
@@ -99,13 +99,12 @@ function usePackGame(id) {
99
99
  var import_auto_paper2 = require("@rific/auto-paper");
100
100
  var import_feedback_press2 = require("@rific/feedback-press");
101
101
  var import_toaster2 = require("@rific/toaster");
102
- var import_core6 = require("@tastic/core");
102
+ var import_core4 = require("@tastic/core");
103
103
  var import_chrome2 = require("@tastic/hud/chrome");
104
104
  var import_profile3 = require("@tastic/profile");
105
105
  var Haptics = __toESM(require("expo-haptics"));
106
106
  var import_expo_router = require("expo-router");
107
- var import_expo_sensors = require("expo-sensors");
108
- var import_react9 = require("react");
107
+ var import_react8 = require("react");
109
108
  var import_react_native2 = require("react-native");
110
109
  var import_react_native_gesture_handler = require("react-native-gesture-handler");
111
110
  var RNPaper2 = __toESM(require("react-native-paper"));
@@ -163,7 +162,6 @@ function GuideHost({ children }) {
163
162
  const packGuide = subject === PACK_GUIDE ? pack.guide : void 0;
164
163
  const game = subject === PACK_GUIDE ? void 0 : pack.games.find((g) => g.id === subject);
165
164
  const gameGuide = game?.guide;
166
- const rotation = (0, import_core.useRotation)();
167
165
  const touch = (0, import_core.useIsTouchPrimaryDevice)();
168
166
  const seatTwo = (0, import_react_redux.useSelector)((state) => state.opponent.seatTwo);
169
167
  const input = (0, import_react_redux.useSelector)((state) => gameGuide ? gameGuide.selectInput(state) : NO_INPUT, import_react_redux.shallowEqual);
@@ -178,7 +176,7 @@ function GuideHost({ children }) {
178
176
  },
179
177
  [dispatch, packGuide, gameGuide]
180
178
  );
181
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_guide.GuideProvider, { steps, currentVersion: (packGuide ?? gameGuide)?.version ?? 0, seenVersion, onSeen, rotation, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GameGuideBridge, { onPick: setSubject, children }) });
179
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_guide.GuideProvider, { steps, currentVersion: (packGuide ?? gameGuide)?.version ?? 0, seenVersion, onSeen, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GameGuideBridge, { onPick: setSubject, children }) });
182
180
  }
183
181
  function GameGuideBridge({ onPick, children }) {
184
182
  const pack = usePack();
@@ -301,66 +299,33 @@ function Theme({ children }) {
301
299
 
302
300
  // src/components/UpdateDialog.tsx
303
301
  var import_toaster = require("@rific/toaster");
304
- var import_core3 = require("@tastic/core");
305
302
  var import_chrome = require("@tastic/hud/chrome");
306
303
  var import_jsx_runtime6 = require("react/jsx-runtime");
307
304
  function UpdateDialog() {
308
305
  const onError = (0, import_toaster.useUpdateErrorToast)();
309
- const rotation = (0, import_core3.useRotation)();
310
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_chrome.UpdateDialog, { onError, rotation });
306
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_chrome.UpdateDialog, { onError });
311
307
  }
312
308
 
313
- // src/hooks/useAppSettings.ts
309
+ // src/hooks/useLastActiveGame.tsx
314
310
  var import_react6 = require("react");
315
311
  var import_react_redux4 = require("react-redux");
316
312
 
317
- // src/redux/appSettingsSlice.ts
318
- var import_core4 = require("@rific/core");
319
- var DEFAULT_APP_SETTINGS = {
320
- lockOrientation: false,
321
- deferBottomEdgeGestures: false
322
- };
323
- var { actions, reducer } = (0, import_core4.createSettingsSlice)("appSettings", {
324
- initialState: DEFAULT_APP_SETTINGS,
325
- initializeMode: "merge",
326
- fieldSetters: []
327
- });
328
- var appSettingsActions = actions;
329
- var appSettingsSlice_default = reducer;
330
-
331
- // src/hooks/useAppSettings.ts
332
- function useAppSettings() {
333
- const settings = (0, import_react_redux4.useSelector)((state) => state.appSettings);
334
- const dispatch = (0, import_react_redux4.useDispatch)();
335
- const setSettings = (0, import_react6.useCallback)(
336
- (patch) => {
337
- dispatch(appSettingsActions.initialize(patch));
338
- },
339
- [dispatch]
340
- );
341
- return { settings, setSettings };
342
- }
343
-
344
- // src/hooks/useLastActiveGame.tsx
345
- var import_react7 = require("react");
346
- var import_react_redux5 = require("react-redux");
347
-
348
313
  // src/redux/lastActiveGameSlice.ts
349
- var import_core5 = require("@rific/core");
350
- var { actions: actions2, reducer: reducer2 } = (0, import_core5.createSettingsSlice)("lastActiveGame", { initialState: { gameId: null } });
351
- var lastActiveGameActions = actions2;
352
- var lastActiveGameSlice_default = reducer2;
314
+ var import_core3 = require("@rific/core");
315
+ var { actions, reducer } = (0, import_core3.createSettingsSlice)("lastActiveGame", { initialState: { gameId: null } });
316
+ var lastActiveGameActions = actions;
317
+ var lastActiveGameSlice_default = reducer;
353
318
 
354
319
  // src/hooks/useLastActiveGame.tsx
355
320
  var import_jsx_runtime7 = require("react/jsx-runtime");
356
- var LastActiveGameContext = (0, import_react7.createContext)(null);
321
+ var LastActiveGameContext = (0, import_react6.createContext)(null);
357
322
  function LastActiveGameProvider({ children }) {
358
- const store = (0, import_react_redux5.useStore)();
359
- const dispatch = (0, import_react_redux5.useDispatch)();
360
- const [initial] = (0, import_react7.useState)(() => store.getState().lastActiveGame?.gameId ?? null);
361
- const pendingResume = (0, import_react7.useRef)(initial);
362
- const persisted = (0, import_react7.useRef)(initial);
363
- const value = (0, import_react7.useMemo)(
323
+ const store = (0, import_react_redux4.useStore)();
324
+ const dispatch = (0, import_react_redux4.useDispatch)();
325
+ const [initial] = (0, import_react6.useState)(() => store.getState().lastActiveGame?.gameId ?? null);
326
+ const pendingResume = (0, import_react6.useRef)(initial);
327
+ const persisted = (0, import_react6.useRef)(initial);
328
+ const value = (0, import_react6.useMemo)(
364
329
  () => ({
365
330
  consumeResumeGame: () => {
366
331
  const game = pendingResume.current;
@@ -388,33 +353,33 @@ function LastActiveGameProvider({ children }) {
388
353
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LastActiveGameContext.Provider, { value, children });
389
354
  }
390
355
  function useLastActiveGame() {
391
- const ctx = (0, import_react7.useContext)(LastActiveGameContext);
356
+ const ctx = (0, import_react6.useContext)(LastActiveGameContext);
392
357
  if (!ctx) throw new Error("useLastActiveGame must be used within a LastActiveGameProvider");
393
358
  return ctx;
394
359
  }
395
360
 
396
361
  // src/hooks/useProfiles.tsx
397
362
  var import_profile2 = require("@tastic/profile");
398
- var import_react8 = require("react");
399
- var import_react_redux6 = require("react-redux");
363
+ var import_react7 = require("react");
364
+ var import_react_redux5 = require("react-redux");
400
365
 
401
366
  // src/redux/profileSelectionSlice.ts
402
367
  var import_profile = require("@tastic/profile");
403
368
  var defaultProfileSelectionState = { 1: null, 2: null };
404
- var { actions: actions3, reducer: reducer3 } = (0, import_profile.createProfileSelectionSlice)("pack", defaultProfileSelectionState);
405
- var profileSelectionActions = actions3;
406
- var profileSelectionSlice_default = reducer3;
369
+ var { actions: actions2, reducer: reducer2 } = (0, import_profile.createProfileSelectionSlice)("pack", defaultProfileSelectionState);
370
+ var profileSelectionActions = actions2;
371
+ var profileSelectionSlice_default = reducer2;
407
372
 
408
373
  // src/hooks/useProfiles.tsx
409
374
  var import_jsx_runtime8 = require("react/jsx-runtime");
410
375
  var SHARED_GROUP_ID = "group.com.infinitetoken.tastic";
411
- var ProfilesContext = (0, import_react8.createContext)(null);
376
+ var ProfilesContext = (0, import_react7.createContext)(null);
412
377
  function ProfilesProvider({ children }) {
413
- const profiles = (0, import_react_redux6.useSelector)((state) => state.profiles);
414
- const lastSelected = (0, import_react_redux6.useSelector)((state) => state.profileSelection);
415
- const dispatch = (0, import_react_redux6.useDispatch)();
416
- const [loaded, setLoaded] = (0, import_react8.useState)(false);
417
- (0, import_react8.useEffect)(() => {
378
+ const profiles = (0, import_react_redux5.useSelector)((state) => state.profiles);
379
+ const lastSelected = (0, import_react_redux5.useSelector)((state) => state.profileSelection);
380
+ const dispatch = (0, import_react_redux5.useDispatch)();
381
+ const [loaded, setLoaded] = (0, import_react7.useState)(false);
382
+ (0, import_react7.useEffect)(() => {
418
383
  let cancelled = false;
419
384
  (0, import_profile2.resolveInitialProfiles)(SHARED_GROUP_ID, profiles).then((resolved) => {
420
385
  if (cancelled) return;
@@ -428,7 +393,7 @@ function ProfilesProvider({ children }) {
428
393
  }, []);
429
394
  const { syncToShared } = (0, import_profile2.useSharedProfilesSync)({
430
395
  groupId: SHARED_GROUP_ID,
431
- onRemoteChange: (0, import_react8.useCallback)(
396
+ onRemoteChange: (0, import_react7.useCallback)(
432
397
  (remote) => {
433
398
  dispatch(import_profile2.profilesActions.setAll(remote));
434
399
  dispatch(profileSelectionActions.pruneToRoster(remote.map((p) => p.id)));
@@ -436,7 +401,7 @@ function ProfilesProvider({ children }) {
436
401
  [dispatch]
437
402
  )
438
403
  });
439
- const createProfile = (0, import_react8.useCallback)(
404
+ const createProfile = (0, import_react7.useCallback)(
440
405
  (input) => {
441
406
  const record = (0, import_profile2.createProfileRecord)(input);
442
407
  dispatch(import_profile2.profilesActions.add(record));
@@ -445,14 +410,14 @@ function ProfilesProvider({ children }) {
445
410
  },
446
411
  [profiles, dispatch, syncToShared]
447
412
  );
448
- const updateProfile = (0, import_react8.useCallback)(
413
+ const updateProfile = (0, import_react7.useCallback)(
449
414
  (id, patch) => {
450
415
  dispatch(import_profile2.profilesActions.update({ id, patch }));
451
416
  syncToShared(profiles.map((p) => p.id === id ? { ...p, ...patch, updatedAt: Date.now() } : p));
452
417
  },
453
418
  [profiles, dispatch, syncToShared]
454
419
  );
455
- const deleteProfile = (0, import_react8.useCallback)(
420
+ const deleteProfile = (0, import_react7.useCallback)(
456
421
  (id) => {
457
422
  dispatch(import_profile2.profilesActions.remove(id));
458
423
  syncToShared(profiles.filter((p) => p.id !== id));
@@ -460,7 +425,7 @@ function ProfilesProvider({ children }) {
460
425
  },
461
426
  [profiles, dispatch, syncToShared]
462
427
  );
463
- const selectProfile = (0, import_react8.useCallback)(
428
+ const selectProfile = (0, import_react7.useCallback)(
464
429
  (seat, profileId) => {
465
430
  dispatch(profileSelectionActions.select({ profileId, seat }));
466
431
  },
@@ -469,7 +434,7 @@ function ProfilesProvider({ children }) {
469
434
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ProfilesContext.Provider, { value: { profiles, lastSelected, loaded, createProfile, updateProfile, deleteProfile, selectProfile }, children });
470
435
  }
471
436
  function useProfiles() {
472
- const ctx = (0, import_react8.useContext)(ProfilesContext);
437
+ const ctx = (0, import_react7.useContext)(ProfilesContext);
473
438
  if (!ctx) throw new Error("useProfiles must be used within a ProfilesProvider");
474
439
  return ctx;
475
440
  }
@@ -485,11 +450,11 @@ function AppStack() {
485
450
  const { Backdrop } = pack;
486
451
  const { dark } = (0, import_auto_paper2.useAutoPaperTheme)();
487
452
  const darkRoot = Backdrop ? true : dark;
488
- const rootBackground = (0, import_core6.useThemedRootBackground)(darkRoot, false);
489
- const statusBar = (0, import_core6.useRotationAwareStatusBar)({ style: darkRoot ? "light" : "dark" });
453
+ const rootBackground = (0, import_core4.useThemedRootBackground)(darkRoot, false);
454
+ const statusBarStyle = darkRoot ? "light" : "dark";
490
455
  const packOptions = (pack.useScreenOptions ?? noScreenOptions)();
491
456
  const backdropOptions = Backdrop ? { contentStyle: TRANSPARENT_CONTENT, animation: import_react_native2.Platform.select({ android: "fade" }) } : void 0;
492
- const playOptions = (0, import_react9.useCallback)(
457
+ const playOptions = (0, import_react8.useCallback)(
493
458
  ({ route }) => {
494
459
  const id = route.params?.game;
495
460
  return pack.games.some((g) => g.id === id && g.keepStatusBar) ? KEEP_STATUS_BAR_OPTIONS : PLAY_OPTIONS;
@@ -497,11 +462,11 @@ function AppStack() {
497
462
  [pack.games]
498
463
  );
499
464
  const navigationBackground = Backdrop ? TRANSPARENT_CONTENT.backgroundColor : rootBackground.contentStyle.backgroundColor;
500
- const navigationTheme = (0, import_react9.useMemo)(() => {
465
+ const navigationTheme = (0, import_react8.useMemo)(() => {
501
466
  const base = darkRoot ? import_expo_router.DarkTheme : import_expo_router.DefaultTheme;
502
467
  return { ...base, colors: { ...base.colors, background: navigationBackground } };
503
468
  }, [darkRoot, navigationBackground]);
504
- const stack = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_expo_router.ThemeProvider, { value: navigationTheme, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_expo_router.Stack, { screenOptions: { ...rootBackground, ...statusBar, ...backdropOptions, ...packOptions }, children: [
469
+ const stack = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_expo_router.ThemeProvider, { value: navigationTheme, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_expo_router.Stack, { screenOptions: { ...rootBackground, statusBarStyle, ...backdropOptions, ...packOptions }, children: [
505
470
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_expo_router.Stack.Screen, { name: "index" }),
506
471
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_expo_router.Stack.Screen, { name: "play/[game]", options: playOptions })
507
472
  ] }) });
@@ -511,11 +476,6 @@ function AppStack() {
511
476
  stack
512
477
  ] });
513
478
  }
514
- function OrientationBridge({ children }) {
515
- const { settings, setSettings } = useAppSettings();
516
- const { portraitWhileKeyboard } = usePack();
517
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_core6.OrientationProvider, { deviceMotion: import_expo_sensors.DeviceMotion, freezeWhileLocked: true, lockInitialValue: { locked: settings.lockOrientation, rotation: settings.lockedRotation }, onLockChange: (next) => setSettings({ lockOrientation: next.locked, lockedRotation: next.rotation }), portraitWhileKeyboard, children });
518
- }
519
479
  function GatedApp({ children }) {
520
480
  const { loaded } = useProfiles();
521
481
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SplashGate, { gate: "profiles", ready: loaded, children });
@@ -526,7 +486,7 @@ function HapticGatedToastProvider({ children }) {
526
486
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_toaster2.ToastProvider, { haptics: settings.vibrate ? Haptics : void 0, paper: PackToasterComponent ? void 0 : RNPaper2, children });
527
487
  }
528
488
  function RotatingToaster() {
529
- const renderFrame = (0, import_react9.useCallback)(
489
+ const renderFrame = (0, import_react8.useCallback)(
530
490
  (toasts) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_chrome2.RotatedFrame, { rotatedSafeArea: true, passThrough: true, children: toasts }),
531
491
  []
532
492
  );
@@ -540,7 +500,7 @@ function GameProviders({ games, children }) {
540
500
  }
541
501
  function PreviewColors({ previewColor, children }) {
542
502
  const { dark } = (0, import_auto_paper2.useAutoPaperTheme)();
543
- const preview = (0, import_react9.useCallback)((hex) => previewColor(hex, dark), [previewColor, dark]);
503
+ const preview = (0, import_react8.useCallback)((hex) => previewColor(hex, dark), [previewColor, dark]);
544
504
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_profile3.ProfileColorProvider, { preview, children });
545
505
  }
546
506
  function PackRoot({ pack, children }) {
@@ -549,11 +509,11 @@ function PackRoot({ pack, children }) {
549
509
  return previewColor ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PreviewColors, { previewColor, children: inner }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: inner });
550
510
  }
551
511
  function RootLayout({ pack, store, persistor }) {
552
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_gesture_handler.GestureHandlerRootView, { style: styles.root, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_safe_area_context.SafeAreaProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PackProvider, { pack, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Fonts, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Providers, { store, persistor, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Theme, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ProfilesProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LastActiveGameProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(GameProviders, { games: pack.games, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PackRoot, { pack, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(GatedApp, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(OrientationBridge, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(HapticGatedToastProvider, { children: [
512
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_gesture_handler.GestureHandlerRootView, { style: styles.root, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_safe_area_context.SafeAreaProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PackProvider, { pack, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Fonts, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Providers, { store, persistor, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Theme, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ProfilesProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LastActiveGameProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(GameProviders, { games: pack.games, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PackRoot, { pack, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(GatedApp, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(HapticGatedToastProvider, { children: [
553
513
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(GuideHost, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AppStack, {}) }),
554
514
  pack.Toaster ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PackToaster, { Toaster: pack.Toaster }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RotatingToaster, {}),
555
515
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(UpdateDialog, {})
556
- ] }) }) }) }) }) }) }) }) }) }) }) }) });
516
+ ] }) }) }) }) }) }) }) }) }) }) }) });
557
517
  }
558
518
  var styles = import_react_native2.StyleSheet.create({
559
519
  root: { flex: 1 }
@@ -572,7 +532,7 @@ var PROFILES_HREF = "/profiles";
572
532
 
573
533
  // src/screens/LoadoutScreen.tsx
574
534
  var import_auto_paper4 = require("@rific/auto-paper");
575
- var import_core10 = require("@tastic/core");
535
+ var import_core9 = require("@tastic/core");
576
536
  var import_chrome4 = require("@tastic/hud/chrome");
577
537
  var import_lobby2 = require("@tastic/hud/lobby");
578
538
  var import_split_screen = require("@tastic/split-screen");
@@ -586,7 +546,7 @@ var import_react_redux9 = require("react-redux");
586
546
  // src/components/SeatTwoPanel.tsx
587
547
  var import_lobby = require("@tastic/hud/lobby");
588
548
  var import_profile4 = require("@tastic/profile");
589
- var import_react10 = require("react");
549
+ var import_react9 = require("react");
590
550
  var import_react_native3 = require("react-native");
591
551
  var import_jsx_runtime10 = require("react/jsx-runtime");
592
552
  var CPU_PREFIX = "cpu:";
@@ -604,7 +564,7 @@ function SeatTwoPanel({ idPrefix, host, choices, seatTwo, onSeatTwoChange, cpuDi
604
564
  const drawn = previewValue ? previewValue(color) : color;
605
565
  const selectedProfile = isPerson ? profiles.find((p) => p.id === selectedProfileId) ?? null : null;
606
566
  const cpuOptions = cpuDifficulty?.options;
607
- const options = (0, import_react10.useMemo)(() => {
567
+ const options = (0, import_react9.useMemo)(() => {
608
568
  const list = [];
609
569
  if (choices.includes("cpu")) list.push(...cpuOptions ? cpuOptions.map((o) => ({ value: `${CPU_PREFIX}${o.value}`, label: o.label, icon: o.icon })) : [{ value: CPU_PREFIX, label: "CPU", icon: CPU_ICON }]);
610
570
  if (choices.includes("nobody")) list.push({ value: NOBODY, label: "Nobody", icon: NOBODY_ICON });
@@ -664,13 +624,45 @@ var styles2 = import_react_native3.StyleSheet.create({
664
624
  // src/components/SettingsDialog.tsx
665
625
  var import_toaster3 = require("@rific/toaster");
666
626
  var import_chrome3 = require("@tastic/hud/chrome");
627
+
628
+ // src/hooks/useAppSettings.ts
629
+ var import_react10 = require("react");
630
+ var import_react_redux6 = require("react-redux");
631
+
632
+ // src/redux/appSettingsSlice.ts
633
+ var import_core5 = require("@rific/core");
634
+ var DEFAULT_APP_SETTINGS = {
635
+ deferBottomEdgeGestures: false
636
+ };
637
+ var { actions: actions3, reducer: reducer3 } = (0, import_core5.createSettingsSlice)("appSettings", {
638
+ initialState: DEFAULT_APP_SETTINGS,
639
+ initializeMode: "merge",
640
+ fieldSetters: []
641
+ });
642
+ var appSettingsActions = actions3;
643
+ var appSettingsSlice_default = reducer3;
644
+
645
+ // src/hooks/useAppSettings.ts
646
+ function useAppSettings() {
647
+ const settings = (0, import_react_redux6.useSelector)((state) => state.appSettings);
648
+ const dispatch = (0, import_react_redux6.useDispatch)();
649
+ const setSettings = (0, import_react10.useCallback)(
650
+ (patch) => {
651
+ dispatch(appSettingsActions.initialize(patch));
652
+ },
653
+ [dispatch]
654
+ );
655
+ return { settings, setSettings };
656
+ }
657
+
658
+ // src/components/SettingsDialog.tsx
667
659
  var import_jsx_runtime11 = require("react/jsx-runtime");
668
- function SettingsDialog({ visible, onDismiss, rotation = 0, onShowHowToPlay, children }) {
660
+ function SettingsDialog({ visible, onDismiss, onShowHowToPlay, children }) {
669
661
  const onUpdateError = (0, import_toaster3.useUpdateErrorToast)();
670
662
  const { settings, setSettings } = useAppSettings();
671
663
  const { otaVersion, SettingsSections, edgeGuard = true } = usePack();
672
664
  const edgeGuardProps = edgeGuard ? { deferBottomEdgeGestures: settings.deferBottomEdgeGestures, onDeferBottomEdgeGestures: (value) => setSettings({ deferBottomEdgeGestures: value }) } : {};
673
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_chrome3.BaseSettingsDialog, { visible, onDismiss, rotation, version: otaVersion, lockOrientation: settings.lockOrientation, onLockOrientationChange: (value) => setSettings({ lockOrientation: value }), ...edgeGuardProps, onUpdateError, onShowHowToPlay, children: [
665
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_chrome3.BaseSettingsDialog, { visible, onDismiss, version: otaVersion, ...edgeGuardProps, onUpdateError, onShowHowToPlay, children: [
674
666
  children,
675
667
  SettingsSections && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SettingsSections, {})
676
668
  ] });
@@ -681,9 +673,9 @@ var import_react11 = require("react");
681
673
  var import_react_redux7 = require("react-redux");
682
674
 
683
675
  // src/redux/opponentSlice.ts
684
- var import_core7 = require("@rific/core");
676
+ var import_core6 = require("@rific/core");
685
677
  var DEFAULT_OPPONENT = { seatTwo: "cpu" };
686
- var { actions: actions4, reducer: reducer4 } = (0, import_core7.createSettingsSlice)("opponent", {
678
+ var { actions: actions4, reducer: reducer4 } = (0, import_core6.createSettingsSlice)("opponent", {
687
679
  initialState: DEFAULT_OPPONENT
688
680
  });
689
681
  var opponentActions = actions4;
@@ -816,13 +808,13 @@ function useSeatColors(p2IsHuman) {
816
808
  }
817
809
 
818
810
  // src/navigation.ts
819
- var import_core8 = require("@rific/core");
811
+ var import_core7 = require("@rific/core");
820
812
  var import_expo_router3 = require("expo-router");
821
- (0, import_core8.configureNavigation)({ router: import_expo_router3.router });
813
+ (0, import_core7.configureNavigation)({ router: import_expo_router3.router });
822
814
 
823
815
  // src/redux/liveplaySlice.ts
824
- var import_core9 = require("@rific/core");
825
- var { actions: actions5, reducer: reducer5 } = (0, import_core9.createSettingsSlice)("liveplay", {
816
+ var import_core8 = require("@rific/core");
817
+ var { actions: actions5, reducer: reducer5 } = (0, import_core8.createSettingsSlice)("liveplay", {
826
818
  initialState: { activelyPlaying: false, activeRound: null }
827
819
  });
828
820
  var liveplayActions = actions5;
@@ -842,14 +834,12 @@ function LoadoutScreen({ game }) {
842
834
  const loadout = game.useLoadout(mode);
843
835
  const { openFor } = useGameGuide();
844
836
  const { profiles, lastSelected, selectProfile } = useProfiles();
845
- const { orientationMode, p1OnRight, upsideDown, resolved: p1OnRightResolved } = (0, import_core10.useOrientationState)();
846
- const fixedSeats = p2IsHuman && (!!game.twoPlayerSeats?.faceToFaceOnWeb || import_react_native4.Platform.OS !== "web");
847
- const fixedUpsideDown = !!game.twoPlayerSeats?.followUpsideDown && orientationMode === "faceToFace" && upsideDown;
848
- const { panelLayout, panelFadeStyle } = (0, import_split_screen.useDualZoneLayout)(fixedSeats ? "faceToFace" : orientationMode, fixedSeats ? true : p1OnRight, fixedSeats || p1OnRightResolved, fixedSeats ? fixedUpsideDown : upsideDown, PANEL_SWAP_FADE_MS);
837
+ const { orientationMode } = (0, import_core9.useOrientationState)();
838
+ const panelMode = p2IsHuman && !game.sideBySideWhenWide ? "faceToFace" : orientationMode;
839
+ const { panelLayout, panelFadeStyle } = (0, import_split_screen.useDualZoneLayout)(panelMode, false, true, false, PANEL_SWAP_FADE_MS);
849
840
  const playersRowFade = panelFadeStyle;
850
841
  const [settingsOpen, setSettingsOpen] = (0, import_react13.useState)(false);
851
- const rotation = (0, import_core10.getViewRotation)(panelLayout.orientationMode, panelLayout.p1OnRight, panelLayout.upsideDown);
852
- const insets = (0, import_core10.rotateInsets)((0, import_react_native_safe_area_context2.useSafeAreaInsets)(), rotation);
842
+ const insets = (0, import_react_native_safe_area_context2.useSafeAreaInsets)();
853
843
  const { colors: themeColors, dark } = (0, import_auto_paper4.useAutoPaperTheme)();
854
844
  const { p1Color, p2Color, handleP1ColorChange, handleP2ColorChange } = useSeatColors(p2IsHuman);
855
845
  const dispatch = (0, import_react_redux9.useDispatch)();
@@ -887,7 +877,7 @@ function LoadoutScreen({ game }) {
887
877
  const triggerStyle = (0, import_react13.useMemo)(() => pack.Backdrop ? [styles3.pill, { backgroundColor: themeColors.surface }] : void 0, [pack.Backdrop, themeColors.surface]);
888
878
  const isPortrait = panelLayout.orientationMode === "faceToFace";
889
879
  const showMetaInSharedBand = (0, import_split_screen.needsSharedNeutralZone)(panelLayout.orientationMode, humanSeats.length);
890
- const p2ZoneStyle = p2IsHuman ? isPortrait ? styles3.p2ZoneTop : panelLayout.p1OnRight ? styles3.p2ZoneLeft : styles3.p2ZoneRight : null;
880
+ const p2ZoneStyle = p2IsHuman ? isPortrait ? styles3.p2ZoneTop : styles3.p2ZoneRight : null;
891
881
  const showReadyButton = p2IsHuman;
892
882
  const seatPickerAlign = p2IsHuman ? void 0 : pack.usePickerAlign;
893
883
  const layoutKey = p2IsHuman ? "zones" : "row";
@@ -897,7 +887,6 @@ function LoadoutScreen({ game }) {
897
887
  const anyPlayerPickerOpen = !!(controlsHost.openId?.startsWith("p1-") || controlsHost.openId?.startsWith("p2-"));
898
888
  const optionsRow = loadout.options({
899
889
  host: controlsHost,
900
- rotation,
901
890
  accentColor: themeColors.tertiary,
902
891
  mutedColor: fgMuted,
903
892
  onAccentColor: themeColors.onTertiary,
@@ -907,17 +896,17 @@ function LoadoutScreen({ game }) {
907
896
  onReset: showMetaInSharedBand ? void 0 : loadout.reset
908
897
  });
909
898
  const optionsPopoverOpen = controlsHost.openId !== null && loadout.optionIds.includes(controlsHost.openId);
910
- const sharedControls = showMetaInSharedBand ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lobby2.SharedActionBand, { onBack: import_core8.safeBack, onSettings: () => setSettingsOpen(true), onRandomize: loadout.randomize, onReset: loadout.reset, fg, popoverOpen: optionsPopoverOpen, children: optionsRow }) : optionsRow;
911
- const controlLook = { accentColor: themeColors.tertiary, mutedColor: fgMuted, dark, rotation };
899
+ const sharedControls = showMetaInSharedBand ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lobby2.SharedActionBand, { onBack: import_core7.safeBack, onSettings: () => setSettingsOpen(true), onRandomize: loadout.randomize, onReset: loadout.reset, fg, popoverOpen: optionsPopoverOpen, children: optionsRow }) : optionsRow;
900
+ const controlLook = { accentColor: themeColors.tertiary, mutedColor: fgMuted, dark };
912
901
  const p1Control = loadout.seatControl(1, controlsHost, { ...controlLook, seatColor: p1Drawn });
913
902
  const p1Panel = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lobby2.PlayerSetupPanel, { idPrefix: "p1", host: controlsHost, color: p1Color, onColorChange: handleP1ColorChange, swatches: import_auto_paper4.defaultColors, takenColor: p2Exists ? p2Color : void 0, allowSwapTaken: mode === "vsCpu", colorCaption: pack.colorCaption, isHuman: true, ready: ready[1], onToggleReady: () => setReady((r) => ({ ...r, 1: !r[1] })), dark, showReadyButton, secondPicker: p1Control, secondPickerId: p1Control ? "p1-control" : void 0, profiles, selectedProfileId: lastSelected[1], takenProfileId: p2IsHuman ? lastSelected[2] : null, guestLabel: pack.guestLabel ?? "P1", nullLabel: pack.nullLabel, onProfileSelect: handleP1ProfileSelect, onManageProfiles: () => import_expo_router4.router.push(PROFILES_HREF), previewValue: seatPreview, triggerStyle, useSeatPickerAlign: seatPickerAlign }, `p1-${layoutKey}`);
914
903
  const p2Control = p2IsHuman ? loadout.seatControl(2, p2Host, { ...controlLook, seatColor: p2Drawn }) : void 0;
915
904
  const p2Panel = game.seatTwo && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SeatTwoPanel, { idPrefix: "p2", host: p2Host, choices: game.seatTwo, seatTwo, onSeatTwoChange: changeSeatTwo, cpuDifficulty: loadout.cpuDifficulty, profiles, selectedProfileId: lastSelected[2], onProfileSelect: handleP2ProfileSelect, color: p2Color, onColorChange: handleP2ColorChange, swatches: import_auto_paper4.defaultColors, takenColor: p1Color, allowSwapTaken: mode === "vsCpu", colorCaption: pack.colorCaption, dark, secondPicker: p2Control, secondPickerId: p2Control ? "p2-control" : void 0, ready: ready[2], onToggleReady: () => setReady((r) => ({ ...r, 2: !r[2] })), showReadyButton, previewValue: seatPreview, triggerStyle, usePickerAlign: seatPickerAlign }, `p2-${layoutKey}`);
916
905
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native4.View, { style: [styles3.container, { backgroundColor: bg }], children: [
917
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_core10.FakeLandscapeView, { orientationMode: panelLayout.orientationMode, p1OnRight: panelLayout.p1OnRight, upsideDown: panelLayout.upsideDown, style: styles3.rotatable, children: [
906
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native4.View, { style: styles3.frame, children: [
918
907
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_chrome4.PressAwayOverlay, { active: controlsHost.openId !== null, onPress: controlsHost.close }),
919
908
  p2ZoneStyle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_chrome4.PressAwayOverlay, { active: controlsHost.openId !== null || p2Host.openId !== null, onPress: p2Host.close, style: p2ZoneStyle }),
920
- !showMetaInSharedBand && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_chrome4.CornerActionButtons, { onBack: import_core8.safeBack, onSettings: () => setSettingsOpen(true), fg, insets }),
909
+ !showMetaInSharedBand && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_chrome4.CornerActionButtons, { onBack: import_core7.safeBack, onSettings: () => setSettingsOpen(true), fg, insets }),
921
910
  p2IsHuman ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
922
911
  import_split_screen.DualZoneLayout,
923
912
  {
@@ -933,13 +922,13 @@ function LoadoutScreen({ game }) {
933
922
  ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native4.View, { style: styles3.stackedZone, children: [
934
923
  sharedControls,
935
924
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native_reanimated2.default.View, { style: [styles3.playersRow, anyPlayerPickerOpen && styles3.playersRowOpen, playersRowFade], children: [
936
- !isPortrait && panelLayout.p1OnRight ? p2Panel : p1Panel,
937
- !isPortrait && panelLayout.p1OnRight ? p1Panel : p2Panel
925
+ p1Panel,
926
+ p2Panel
938
927
  ] }),
939
928
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lobby2.ReadyButton, { color: p1Drawn, ready: ready[1], onToggleReady: () => setReady((r) => ({ ...r, 1: !r[1] })), style: anyPlayerPickerOpen && styles3.readyButtonHidden })
940
929
  ] })
941
930
  ] }),
942
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false), rotation, onShowHowToPlay: () => openFor(game.id) })
931
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false), onShowHowToPlay: () => openFor(game.id) })
943
932
  ] });
944
933
  }
945
934
  function halfOf(color) {
@@ -950,12 +939,13 @@ var styles3 = import_react_native4.StyleSheet.create({
950
939
  container: {
951
940
  flex: 1
952
941
  },
953
- p2ZoneLeft: {
954
- bottom: 0,
955
- left: 0,
956
- position: "absolute",
957
- top: 0,
958
- width: "50%"
942
+ // A real full-bleed box to size the absolutely-positioned children against.
943
+ frame: {
944
+ alignItems: "center",
945
+ flex: 1,
946
+ gap: 32,
947
+ justifyContent: "center",
948
+ paddingHorizontal: 16
959
949
  },
960
950
  p2ZoneRight: {
961
951
  bottom: 0,
@@ -990,14 +980,6 @@ var styles3 = import_react_native4.StyleSheet.create({
990
980
  opacity: 0,
991
981
  pointerEvents: "none"
992
982
  },
993
- // A real full-bleed box for FakeLandscapeView to size its absolutely-positioned children against.
994
- rotatable: {
995
- alignItems: "center",
996
- flex: 1,
997
- gap: 32,
998
- justifyContent: "center",
999
- paddingHorizontal: 16
1000
- },
1001
983
  stackedZone: {
1002
984
  alignItems: "center",
1003
985
  gap: 28
@@ -1038,12 +1020,11 @@ function useResumePoint(gameId) {
1038
1020
  // src/screens/HomeScreen.tsx
1039
1021
  var import_auto_paper5 = require("@rific/auto-paper");
1040
1022
  var import_feedback_press3 = require("@rific/feedback-press");
1041
- var import_core12 = require("@tastic/core");
1042
1023
  var import_chrome5 = require("@tastic/hud/chrome");
1043
1024
  var import_profile7 = require("@tastic/profile");
1044
1025
  var import_expo_router6 = require("expo-router");
1045
1026
  var import_react16 = require("react");
1046
- var import_react_native6 = require("react-native");
1027
+ var import_react_native7 = require("react-native");
1047
1028
  var import_react_native_paper = require("react-native-paper");
1048
1029
 
1049
1030
  // src/hooks/useFrozenWhileHidden.ts
@@ -1063,7 +1044,8 @@ function useFrozenWhileHidden(value, hidden) {
1063
1044
  }
1064
1045
 
1065
1046
  // src/screens/homeLayout.ts
1066
- var import_core11 = require("@tastic/core");
1047
+ var import_core10 = require("@tastic/core");
1048
+ var import_react_native6 = require("react-native");
1067
1049
  var import_react_native_safe_area_context3 = require("react-native-safe-area-context");
1068
1050
  var PREVIEW_ASPECT = 360 / 200;
1069
1051
  var ROW_PREVIEW_WIDTH = 92;
@@ -1092,9 +1074,9 @@ function computeHomeLayout(windowWidth, windowHeight, insets) {
1092
1074
  const inner = Math.max(0, usable - HOME_WIDE_EDGE_PADDING - HOME_WIDE_RIGHT_GUTTER);
1093
1075
  const content = Math.min(inner, HOME_WIDE_MAX_CONTENT_WIDTH);
1094
1076
  const slack = (inner - content) / 2;
1095
- const leftPaneWidth = Math.min((0, import_core11.clamp)(Math.round(HOME_LEFT_PANE_FRACTION * content), HOME_LEFT_PANE_MIN, HOME_LEFT_PANE_MAX), Math.max(0, content - HOME_WIDE_PANE_GAP - HOME_MIN_TILE_WIDTH));
1077
+ const leftPaneWidth = Math.min((0, import_core10.clamp)(Math.round(HOME_LEFT_PANE_FRACTION * content), HOME_LEFT_PANE_MIN, HOME_LEFT_PANE_MAX), Math.max(0, content - HOME_WIDE_PANE_GAP - HOME_MIN_TILE_WIDTH));
1096
1078
  const rightPaneWidth = Math.max(0, content - HOME_WIDE_PANE_GAP - leftPaneWidth);
1097
- const columns = (0, import_core11.clamp)(Math.floor((rightPaneWidth + HOME_GRID_GAP) / (HOME_MIN_TILE_WIDTH + HOME_GRID_GAP)), 1, HOME_MAX_COLUMNS);
1079
+ const columns = (0, import_core10.clamp)(Math.floor((rightPaneWidth + HOME_GRID_GAP) / (HOME_MIN_TILE_WIDTH + HOME_GRID_GAP)), 1, HOME_MAX_COLUMNS);
1098
1080
  const tileWidth = Math.max(0, Math.floor((rightPaneWidth - (columns - 1) * HOME_GRID_GAP) / columns));
1099
1081
  const previewWidth = Math.max(0, tileWidth - 2 * HOME_TILE_PADDING);
1100
1082
  const heroFit = {
@@ -1117,9 +1099,8 @@ function computeHomeLayout(windowWidth, windowHeight, insets) {
1117
1099
  };
1118
1100
  }
1119
1101
  function useHomeLayout() {
1120
- const { width, height } = (0, import_core11.useRotatedWindowDimensions)();
1121
- const rotation = (0, import_core11.useRotation)();
1122
- const insets = (0, import_core11.rotateInsets)((0, import_react_native_safe_area_context3.useSafeAreaInsets)(), rotation);
1102
+ const { width, height } = (0, import_react_native6.useWindowDimensions)();
1103
+ const insets = (0, import_react_native_safe_area_context3.useSafeAreaInsets)();
1123
1104
  return computeHomeLayout(width, height, insets);
1124
1105
  }
1125
1106
 
@@ -1141,7 +1122,6 @@ function HomeScreen() {
1141
1122
  }
1142
1123
  function HomeMenu() {
1143
1124
  const pack = usePack();
1144
- const rotation = (0, import_core12.useRotation)();
1145
1125
  const layout = useHomeLayout();
1146
1126
  const { insets } = layout;
1147
1127
  const { dark, colors } = (0, import_auto_paper5.useAutoPaperTheme)();
@@ -1149,9 +1129,9 @@ function HomeMenu() {
1149
1129
  const [settingsOpen, setSettingsOpen] = (0, import_react16.useState)(false);
1150
1130
  const [rulesGame, setRulesGame] = (0, import_react16.useState)(null);
1151
1131
  const isFocused = (0, import_expo_router6.useIsFocused)();
1152
- const [appState, setAppState] = (0, import_react16.useState)(import_react_native6.AppState.currentState);
1132
+ const [appState, setAppState] = (0, import_react16.useState)(import_react_native7.AppState.currentState);
1153
1133
  (0, import_react16.useEffect)(() => {
1154
- const subscription = import_react_native6.AppState.addEventListener("change", setAppState);
1134
+ const subscription = import_react_native7.AppState.addEventListener("change", setAppState);
1155
1135
  return () => subscription.remove();
1156
1136
  }, []);
1157
1137
  const heroActive = isFocused && appState === "active";
@@ -1171,12 +1151,19 @@ function HomeMenu() {
1171
1151
  const closePicker = pickerHost.close;
1172
1152
  (0, import_react16.useEffect)(() => {
1173
1153
  closePicker();
1174
- }, [isWide, rotation, layout.windowWidth, layout.windowHeight, closePicker]);
1154
+ }, [isWide, layout.windowWidth, layout.windowHeight, closePicker]);
1175
1155
  const bg = pack.Backdrop ? "transparent" : dark ? "#000000" : "#FFFFFF";
1176
1156
  const fg = pack.foreground ?? (dark ? "#FFFFFF" : "#000000");
1177
1157
  const { previewColor } = pack;
1178
1158
  const p1Drawn = previewColor ? previewColor(p1Color, dark) : p1Color;
1179
1159
  const triggerStyle = (0, import_react16.useMemo)(() => pack.Backdrop ? [styles4.pill, { backgroundColor: colors.surface }] : void 0, [pack.Backdrop, colors.surface]);
1160
+ const tabs = pack.homeTabs && pack.homeTabs.length > 1 ? pack.homeTabs : null;
1161
+ const [tabLabel, setTabLabel] = (0, import_react16.useState)(() => tabs?.[0].label ?? null);
1162
+ const tab = tabs ? tabs.find((t) => t.label === tabLabel) ?? tabs[0] : null;
1163
+ const tabButtons = (0, import_react16.useMemo)(() => tabs?.map(({ label }) => ({ value: label, label })) ?? [], [tabs]);
1164
+ const tabBar = tab && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper.SegmentedButtons, { value: tab.label, onValueChange: setTabLabel, buttons: tabButtons, style: [styles4.tabs, pack.Backdrop && { backgroundColor: colors.surface }] });
1165
+ const columnClamp = useClampOnShrink();
1166
+ const gridClamp = useClampOnShrink();
1180
1167
  const openGame = (0, import_react16.useCallback)((game) => openFirstTime(game.id, () => import_expo_router6.router.push(game.useLoadout ? loadoutHref(game.id) : playHref(game.id))), [openFirstTime]);
1181
1168
  const openRules = (0, import_react16.useCallback)((game) => game.Rules ? setRulesGame(game) : openFor(game.id), [openFor]);
1182
1169
  const colorSection = { value: p1Color, onChange: handleP1ColorChange, swatches: import_auto_paper5.defaultColors, caption: selectedProfile ? pack.colorCaption : void 0, previewValue: previewColor ? (hex) => previewColor(hex, dark) : void 0 };
@@ -1186,46 +1173,78 @@ function HomeMenu() {
1186
1173
  const picker = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_profile7.ProfilePicker, { idPrefix: SEAT_ONE_PICKER, host: pickerHost, profiles, selectedId: lastSelected[1], color: p1Drawn, dark, guestLabel: pack.guestLabel ?? "P1", nullLabel: pack.nullLabel, onSelect: (profile) => selectProfile(1, profile?.id ?? null), onManage: () => import_expo_router6.router.push(PROFILES_HREF), colorSection, alignOverride: pickerAlign, triggerStyle });
1187
1174
  const pressAway = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_chrome5.PressAwayOverlay, { active: pickerHost.openId !== null, onPress: pickerHost.close, style: styles4.pressAway });
1188
1175
  const RulesSheet = rulesGame?.Rules;
1189
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { style: [styles4.container, { backgroundColor: bg }], children: [
1190
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_core12.FakeLandscapeView, { style: styles4.rotatable, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(RulesHost, { enabled: pack.games.some((game) => game.Rules), children: [
1176
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native7.View, { style: [styles4.container, { backgroundColor: bg }], children: [
1177
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: styles4.frame, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(RulesHost, { enabled: pack.games.some((game) => game.Rules), children: [
1191
1178
  isWide ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
1192
1179
  pressAway,
1193
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_auto_paper5.FadingScrollView, { style: [styles4.wideRightPane, { left: layout.paddingLeft + layout.leftPaneWidth + HOME_WIDE_PANE_GAP }], contentContainerStyle: { paddingTop: 16 + insets.top, paddingBottom: 16 + insets.bottom, paddingRight: layout.paddingRight }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.grid, { width: layout.rightPaneWidth }], children: pack.games.map((game) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameTile, { game, width: layout.tileWidth, previewWidth: layout.previewWidth, previewRevealed: previewsRevealed, dark, p1Color, p2Color, onPress: () => openGame(game), onRules: hasRules(game) ? () => openRules(game) : void 0 }, game.id)) }) }),
1194
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { style: [styles4.wideLeftPane, { left: layout.paddingLeft, width: layout.leftPaneWidth, paddingTop: HOME_TOP_BAND + insets.top, paddingBottom: 16 + insets.bottom }, pickerHost.openId !== null && styles4.pickerOpen], children: [
1180
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_auto_paper5.FadingScrollView, { ...tab && gridClamp, style: [styles4.wideRightPane, { left: layout.paddingLeft + layout.leftPaneWidth + HOME_WIDE_PANE_GAP }], contentContainerStyle: { paddingTop: 16 + insets.top, paddingBottom: 16 + insets.bottom, paddingRight: layout.paddingRight }, children: [
1181
+ tabBar && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: [styles4.wideTabs, { width: layout.rightPaneWidth }], children: tabBar }),
1182
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: [styles4.grid, { width: layout.rightPaneWidth }], children: pack.games.map((game) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameTile, { game, hidden: isHidden(tab, game), width: layout.tileWidth, previewWidth: layout.previewWidth, previewRevealed: previewsRevealed, dark, p1Color, p2Color, onPress: () => openGame(game), onRules: hasRules(game) ? () => openRules(game) : void 0 }, game.id)) })
1183
+ ] }),
1184
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native7.View, { style: [styles4.wideLeftPane, { left: layout.paddingLeft, width: layout.leftPaneWidth, paddingTop: HOME_TOP_BAND + insets.top, paddingBottom: 16 + insets.bottom }, pickerHost.openId !== null && styles4.pickerOpen], children: [
1195
1185
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(pack.HeroTitle, { words: pack.wordmark, color: fg, p1Color, p2Color, active: heroActive, fit: layout.heroFit }),
1196
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: styles4.widePicker, children: picker })
1186
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: styles4.widePicker, children: picker })
1197
1187
  ] })
1198
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_auto_paper5.FadingScrollView, { style: styles4.scroll, contentContainerStyle: [styles4.content, { paddingTop: 56 + insets.top, paddingBottom: 24 + insets.bottom, paddingLeft: 16 + insets.left, paddingRight: 16 + insets.right }], children: [
1188
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_auto_paper5.FadingScrollView, { ...tab && columnClamp, style: styles4.scroll, contentContainerStyle: [styles4.content, { paddingTop: 56 + insets.top, paddingBottom: 24 + insets.bottom, paddingLeft: 16 + insets.left, paddingRight: 16 + insets.right }], children: [
1199
1189
  pressAway,
1200
1190
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(pack.HeroTitle, { words: pack.wordmark, color: fg, p1Color, p2Color, active: heroActive }),
1201
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.picker, pickerHost.openId !== null && styles4.pickerOpen], children: picker }),
1202
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: styles4.list, children: pack.games.map((game) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameRow, { game, dark, p1Color, p2Color, previewRevealed: previewsRevealed, onPress: () => openGame(game), onRules: hasRules(game) ? () => openRules(game) : void 0 }, game.id)) })
1191
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: [styles4.picker, pickerHost.openId !== null && styles4.pickerOpen], children: picker }),
1192
+ tabBar && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: styles4.listWidth, children: tabBar }),
1193
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: styles4.list, children: pack.games.map((game) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameRow, { game, hidden: isHidden(tab, game), dark, p1Color, p2Color, previewRevealed: previewsRevealed, onPress: () => openGame(game), onRules: hasRules(game) ? () => openRules(game) : void 0 }, game.id)) })
1203
1194
  ] }),
1204
1195
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press3.IconButton, { icon: "trophy", iconColor: fg, size: 24, style: [styles4.corner, { top: 8 + insets.top, left: 8 + insets.left }], onPress: () => import_expo_router6.router.push(STATS_HREF), accessibilityLabel: "Stats & Achievements" }),
1205
1196
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press3.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles4.corner, { top: 8 + insets.top, right: 8 + insets.right }], onPress: () => setSettingsOpen(true), accessibilityLabel: "Settings" }),
1206
1197
  RulesSheet && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(RulesSheet, { visible: true, onDismiss: () => setRulesGame(null) })
1207
1198
  ] }) }),
1208
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false), rotation, onShowHowToPlay: pack.guide ? openPack : void 0 })
1199
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false), onShowHowToPlay: pack.guide ? openPack : void 0 })
1209
1200
  ] });
1210
1201
  }
1211
1202
  var SEAT_ONE_PICKER = "home-p1";
1212
1203
  var noPickerAlign = () => void 0;
1204
+ function isHidden(tab, game) {
1205
+ return !!tab && !tab.includes(game);
1206
+ }
1207
+ function useClampOnShrink() {
1208
+ const view = (0, import_react16.useRef)(null);
1209
+ const sizes = (0, import_react16.useRef)({ viewport: 0, offset: 0 });
1210
+ return (0, import_react16.useMemo)(
1211
+ () => ({
1212
+ ref: (instance) => {
1213
+ view.current = instance;
1214
+ sizes.current.offset = 0;
1215
+ },
1216
+ onLayout: (e) => {
1217
+ sizes.current.viewport = e.nativeEvent.layout.height;
1218
+ },
1219
+ onScroll: (e) => {
1220
+ sizes.current.offset = e.nativeEvent.contentOffset.y;
1221
+ },
1222
+ onContentSizeChange: (_width, height) => {
1223
+ const end = Math.max(0, height - sizes.current.viewport);
1224
+ if (sizes.current.offset <= end) return;
1225
+ sizes.current.offset = end;
1226
+ view.current?.scrollTo({ y: end, animated: false });
1227
+ }
1228
+ }),
1229
+ []
1230
+ );
1231
+ }
1213
1232
  function hasRules(game) {
1214
1233
  return !!(game.Rules || game.guide);
1215
1234
  }
1216
1235
  function RulesHost({ enabled, children }) {
1217
1236
  return enabled ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper.Portal.Host, { children }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children });
1218
1237
  }
1219
- function GameRow({ game, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
1238
+ function GameRow({ game, hidden, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
1220
1239
  const well = (0, import_chrome5.useWellColors)();
1221
1240
  const GameIcon = game.icon;
1222
1241
  return (
1223
1242
  // The rules button sits beside the row's own button, laid over its end, not inside it: a button
1224
1243
  // inside a button is invalid on the web.
1225
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { children: [
1244
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native7.View, { style: hidden ? styles4.hidden : void 0, children: [
1226
1245
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_feedback_press3.Pressable, { onPress, style: [styles4.row, { backgroundColor: well.bg }], accessibilityRole: "button", accessibilityLabel: game.title, children: [
1227
- game.Preview ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PreviewBox, { game, width: ROW_PREVIEW_WIDTH, revealed: previewRevealed, dark, p1Color, p2Color }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.rowIcon, { backgroundColor: dark ? ICON_DARK_BG : ICON_LIGHT_BG }], children: GameIcon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameIcon, { size: ROW_ICON_SIZE, p1Color, p2Color, dark }) }),
1228
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { style: styles4.rowText, children: [
1246
+ game.Preview ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PreviewBox, { game, width: ROW_PREVIEW_WIDTH, revealed: previewRevealed, dark, p1Color, p2Color }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: [styles4.rowIcon, { backgroundColor: dark ? ICON_DARK_BG : ICON_LIGHT_BG }], children: GameIcon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameIcon, { size: ROW_ICON_SIZE, p1Color, p2Color, dark }) }),
1247
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native7.View, { style: styles4.rowText, children: [
1229
1248
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper.Text, { variant: "titleLarge", style: [styles4.rowTitle, { color: well.fg }], numberOfLines: 1, children: game.title }),
1230
1249
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper.Text, { variant: "bodyMedium", style: { color: well.fgMuted }, numberOfLines: 2, children: game.blurb })
1231
1250
  ] })
@@ -1234,9 +1253,9 @@ function GameRow({ game, dark, p1Color, p2Color, previewRevealed, onPress, onRul
1234
1253
  ] })
1235
1254
  );
1236
1255
  }
1237
- function GameTile({ game, width, previewWidth, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
1256
+ function GameTile({ game, hidden, width, previewWidth, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
1238
1257
  const well = (0, import_chrome5.useWellColors)();
1239
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { style: { width }, children: [
1258
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native7.View, { style: [{ width }, hidden && styles4.hidden], children: [
1240
1259
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_feedback_press3.Pressable, { onPress, style: [styles4.tile, { backgroundColor: well.bg }], accessibilityRole: "button", accessibilityLabel: game.title, children: [
1241
1260
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PreviewBox, { game, width: previewWidth, revealed: previewRevealed, dark, p1Color, p2Color }),
1242
1261
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper.Text, { variant: "titleMedium", style: [styles4.rowTitle, { color: well.fg }], numberOfLines: 1, children: game.title }),
@@ -1249,13 +1268,13 @@ function PreviewBox({ game, width, revealed, dark, p1Color, p2Color }) {
1249
1268
  const height = Math.round(width / PREVIEW_ASPECT);
1250
1269
  const { Preview, icon: GameIcon } = game;
1251
1270
  const iconSize = Math.min(height, ROW_ICON_SIZE);
1252
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.preview, { width, height, backgroundColor: dark ? ICON_DARK_BG : ICON_LIGHT_BG }], children: Preview ? revealed && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Preview, { width, height, p1Color, p2Color, dark }) : GameIcon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.previewIcon, { width: iconSize, height: iconSize, borderRadius: iconSize * 0.22 }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameIcon, { size: iconSize, p1Color, p2Color, dark }) }) });
1271
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: [styles4.preview, { width, height, backgroundColor: dark ? ICON_DARK_BG : ICON_LIGHT_BG }], children: Preview ? revealed && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Preview, { width, height, p1Color, p2Color, dark }) : GameIcon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: [styles4.previewIcon, { width: iconSize, height: iconSize, borderRadius: iconSize * 0.22 }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameIcon, { size: iconSize, p1Color, p2Color, dark }) }) });
1253
1272
  }
1254
1273
  var ROW_ICON_SIZE = 56;
1255
1274
  var ICON_LIGHT_BG = "#FAFAFA";
1256
1275
  var ICON_DARK_BG = "#050505";
1257
1276
  var RULES_BUTTON_SPACE = 44;
1258
- var styles4 = import_react_native6.StyleSheet.create({
1277
+ var styles4 = import_react_native7.StyleSheet.create({
1259
1278
  container: {
1260
1279
  flex: 1
1261
1280
  },
@@ -1266,18 +1285,30 @@ var styles4 = import_react_native6.StyleSheet.create({
1266
1285
  corner: {
1267
1286
  position: "absolute"
1268
1287
  },
1288
+ frame: {
1289
+ flex: 1
1290
+ },
1269
1291
  // The wide layout's tiles: wrapped rows of tiles the layout sizes to fill the pane's width.
1270
1292
  grid: {
1271
1293
  flexDirection: "row",
1272
1294
  flexWrap: "wrap",
1273
1295
  gap: HOME_GRID_GAP
1274
1296
  },
1297
+ // Out of the layout (a grid closes up around it) and hidden from screen readers.
1298
+ hidden: {
1299
+ display: "none"
1300
+ },
1275
1301
  // Solitaire's list: one column, never wider than a phone's, centred on a wide screen.
1276
1302
  list: {
1277
1303
  gap: 14,
1278
1304
  maxWidth: 480,
1279
1305
  width: "100%"
1280
1306
  },
1307
+ // The list's width, for the tabs over it.
1308
+ listWidth: {
1309
+ maxWidth: 480,
1310
+ width: "100%"
1311
+ },
1281
1312
  picker: {
1282
1313
  alignItems: "center"
1283
1314
  },
@@ -1303,9 +1334,6 @@ var styles4 = import_react_native6.StyleSheet.create({
1303
1334
  previewIcon: {
1304
1335
  overflow: "hidden"
1305
1336
  },
1306
- rotatable: {
1307
- flex: 1
1308
- },
1309
1337
  // A well (@tastic/hud's wellSurface): the theme's surface on MD3's level-1 shadow, the same surface
1310
1338
  // as Solitaire's game rows, the stats screen's sections and every dialog.
1311
1339
  row: {
@@ -1340,6 +1368,10 @@ var styles4 = import_react_native6.StyleSheet.create({
1340
1368
  scroll: {
1341
1369
  flex: 1
1342
1370
  },
1371
+ // SegmentedButtons' own rounding, so a surface behind it is the same pill (the stats screen's tabs).
1372
+ tabs: {
1373
+ borderRadius: 20
1374
+ },
1343
1375
  // The same well as a row, turned on its side.
1344
1376
  tile: {
1345
1377
  borderRadius: 12,
@@ -1378,6 +1410,10 @@ var styles4 = import_react_native6.StyleSheet.create({
1378
1410
  position: "absolute",
1379
1411
  right: 0,
1380
1412
  top: 0
1413
+ },
1414
+ // The tabs over the grid, a little further from it than the tiles are from each other.
1415
+ wideTabs: {
1416
+ marginBottom: 16
1381
1417
  }
1382
1418
  });
1383
1419
 
@@ -1394,7 +1430,7 @@ function useRosterAlign(open, contentWidth, contentHeight) {
1394
1430
  function ProfilesRoute() {
1395
1431
  const { profiles, createProfile, updateProfile, deleteProfile } = useProfiles();
1396
1432
  const { Backdrop, foreground, newProfileColor, usePickerAlign } = usePack();
1397
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_chrome6.RotatedFrame, { rotatedSafeArea: true, portalHost: true, maxContentWidth: ROSTER_MAX_WIDTH, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_profile8.ProfilesScreen, { profiles, defaultColor: newProfileColor ?? import_auto_paper6.defaultColors[0].value, onCreate: (patch) => createProfile(patch), onSave: (id, patch) => updateProfile(id, patch), onDelete: deleteProfile, onBack: import_core8.safeBack, fg: foreground, bg: Backdrop ? "transparent" : void 0, useColorPickerAlign: usePickerAlign ? useRosterAlign : void 0 }) });
1433
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_chrome6.RotatedFrame, { rotatedSafeArea: true, portalHost: true, maxContentWidth: ROSTER_MAX_WIDTH, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_profile8.ProfilesScreen, { profiles, defaultColor: newProfileColor ?? import_auto_paper6.defaultColors[0].value, onCreate: (patch) => createProfile(patch), onSave: (id, patch) => updateProfile(id, patch), onDelete: deleteProfile, onBack: import_core7.safeBack, fg: foreground, bg: Backdrop ? "transparent" : void 0, useColorPickerAlign: usePickerAlign ? useRosterAlign : void 0 }) });
1398
1434
  }
1399
1435
 
1400
1436
  // src/screens/StatsScreen.tsx
@@ -1402,7 +1438,7 @@ var import_auto_paper7 = require("@rific/auto-paper");
1402
1438
  var import_chrome7 = require("@tastic/hud/chrome");
1403
1439
  var import_profile9 = require("@tastic/profile");
1404
1440
  var import_react17 = require("react");
1405
- var import_react_native7 = require("react-native");
1441
+ var import_react_native8 = require("react-native");
1406
1442
  var import_react_native_paper2 = require("react-native-paper");
1407
1443
  var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
1408
1444
  var import_react_redux10 = require("react-redux");
@@ -1425,7 +1461,7 @@ function StatsScreen() {
1425
1461
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatsPressAway, { host: pickerHost, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_chrome7.RotatedFrame, { rotatedSafeArea: true, portalHost: true, maxContentWidth: STATS_MAX_WIDTH, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatsContent, { pickerHost }) }) });
1426
1462
  }
1427
1463
  function StatsPressAway({ host, children }) {
1428
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native7.View, { testID: "stats-press-away", style: styles5.screen, onStartShouldSetResponder: () => host.openId !== null, onResponderRelease: host.close, children });
1464
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native8.View, { testID: "stats-press-away", style: styles5.screen, onStartShouldSetResponder: () => host.openId !== null, onResponderRelease: host.close, children });
1429
1465
  }
1430
1466
  var useNoPackReset = () => null;
1431
1467
  var noPickerAlign2 = () => void 0;
@@ -1459,10 +1495,10 @@ function StatsContent({ pickerHost }) {
1459
1495
  const surface = pack.Backdrop ? { backgroundColor: colors.surface } : void 0;
1460
1496
  const triggerStyle = pack.Backdrop ? [styles5.pill, surface] : void 0;
1461
1497
  const PackTabContent = packTab?.Content;
1462
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_chrome7.BaseStatsScreen, { title, onBack: import_core8.safeBack, insets, onReset: reset.onReset, resetLabel: reset.label, resetConfirmTitle: reset.title, resetConfirmBody: reset.body, fg: pack.foreground, bg: pack.Backdrop ? "transparent" : void 0, rotation: 0, children: [
1498
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_chrome7.BaseStatsScreen, { title, onBack: import_core7.safeBack, insets, onReset: reset.onReset, resetLabel: reset.label, resetConfirmTitle: reset.title, resetConfirmBody: reset.body, fg: pack.foreground, bg: pack.Backdrop ? "transparent" : void 0, rotation: 0, children: [
1463
1499
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_chrome7.PressAwayOverlay, { active: pickerHost.openId !== null, onPress: pickerHost.close, style: styles5.pressAway }),
1464
1500
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper2.SegmentedButtons, { value: tab, onValueChange: setTab, buttons: tabButtons, style: [styles5.tabs, surface] }),
1465
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native7.View, { style: [styles5.filters, pickerHost.openId !== null && styles5.filtersOpen], onStartShouldSetResponder: () => pickerHost.openId !== null, children: [
1501
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native8.View, { style: [styles5.filters, pickerHost.openId !== null && styles5.filtersOpen], onStartShouldSetResponder: () => pickerHost.openId !== null, children: [
1466
1502
  profiles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_profile9.OptionPicker, { id: PLAYER_PICKER_ID, host: pickerHost, options: playerOptions, value: profileId ?? EVERYONE, onChange: (value) => setSelectedProfileId(value === EVERYONE ? null : value), color: colors.primary, dark, icon: profileId ? "account" : "account-group", alignOverride: playerAlign, triggerStyle }),
1467
1503
  showGamePicker && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_profile9.OptionPicker, { id: GAME_PICKER_ID, host: pickerHost, options: gameOptions, value: gameId, onChange: setGameId, color: colors.primary, dark, icon: "view-grid-outline", alignOverride: gameAlign, triggerStyle })
1468
1504
  ] }),
@@ -1483,12 +1519,12 @@ function GameStats({ game, tab, profileId, showTitle, titleColor }) {
1483
1519
  const { StatsSections, AchievementList } = game;
1484
1520
  const Sections = tab === STATS ? StatsSections : AchievementList;
1485
1521
  if (!Sections) return null;
1486
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native7.View, { style: styles5.game, children: [
1522
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native8.View, { style: styles5.game, children: [
1487
1523
  showTitle && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper2.Text, { variant: "titleLarge", style: [styles5.gameTitle, { color: titleColor }], children: game.title }),
1488
1524
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Sections, { profileId })
1489
1525
  ] });
1490
1526
  }
1491
- var styles5 = import_react_native7.StyleSheet.create({
1527
+ var styles5 = import_react_native8.StyleSheet.create({
1492
1528
  filters: {
1493
1529
  flexDirection: "row",
1494
1530
  flexWrap: "wrap",
@@ -1522,11 +1558,11 @@ var styles5 = import_react_native7.StyleSheet.create({
1522
1558
  });
1523
1559
 
1524
1560
  // src/components/imageGameIcon.tsx
1525
- var import_react_native8 = require("react-native");
1561
+ var import_react_native9 = require("react-native");
1526
1562
  var import_jsx_runtime17 = require("react/jsx-runtime");
1527
1563
  function imageGameIcon(images) {
1528
1564
  return function ImageGameIcon({ size, dark }) {
1529
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native8.Image, { source: dark ? images.dark : images.light, style: { height: size, width: size }, accessibilityIgnoresInvertColors: true });
1565
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native9.Image, { source: dark ? images.dark : images.light, style: { height: size, width: size }, accessibilityIgnoresInvertColors: true });
1530
1566
  };
1531
1567
  }
1532
1568
 
@@ -1546,7 +1582,7 @@ var import_redux_persist = require("redux-persist");
1546
1582
 
1547
1583
  // src/redux/shellState.ts
1548
1584
  var import_auto_paper8 = require("@rific/auto-paper");
1549
- var import_core13 = require("@rific/core");
1585
+ var import_core11 = require("@rific/core");
1550
1586
  var import_feedback_press4 = require("@rific/feedback-press");
1551
1587
  var import_profile10 = require("@tastic/profile");
1552
1588
  var initialSoundSettings = { ...import_feedback_press4.defaultSoundSettings, enabled: !__DEV__ };
@@ -1567,7 +1603,7 @@ function createShellReducers({ seatColors, appearance = "system", seatTheme }) {
1567
1603
  sound: appSoundReducer,
1568
1604
  // @rific/core's Keep Awake setting (see Providers.tsx's KeepAwakeBridge), under the namespace its
1569
1605
  // REHYDRATE handling expects.
1570
- keepAwake: import_core13.keepAwakeReducer,
1606
+ keepAwake: import_core11.keepAwakeReducer,
1571
1607
  // The shared App Group roster (see @tastic/profile's resolveInitialProfiles/
1572
1608
  // useSharedProfilesSync, wired in hooks/useProfiles.tsx) is layered on top of this as a separate
1573
1609
  // sync target - this slice persisting normally via redux-persist is what gives it local-fallback