@tastic/shell 0.4.0 → 0.6.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
@@ -35,6 +35,9 @@ __export(index_exports, {
35
35
  GUEST_PLAYER_ID: () => GUEST_PLAYER_ID,
36
36
  HomeScreen: () => HomeScreen,
37
37
  LoadoutRoute: () => LoadoutRoute,
38
+ MATCH_CHIPS_DESKTOP_MIN_WIDTH: () => MATCH_CHIPS_DESKTOP_MIN_WIDTH,
39
+ MATCH_CHIPS_FADE_MS: () => MATCH_CHIPS_FADE_MS,
40
+ MatchChips: () => MatchChips,
38
41
  PREVIEW_ASPECT: () => PREVIEW_ASPECT,
39
42
  PackProvider: () => PackProvider,
40
43
  PlayRoute: () => PlayRoute,
@@ -64,7 +67,7 @@ __export(index_exports, {
64
67
  profileSelectionActions: () => profileSelectionActions,
65
68
  profileSelectionReducer: () => profileSelectionSlice_default,
66
69
  roundModeFor: () => roundModeFor,
67
- safeBack: () => import_core8.safeBack,
70
+ safeBack: () => import_core7.safeBack,
68
71
  seatColorsActions: () => seatColorsActions,
69
72
  seatPlayerKey: () => seatPlayerKey,
70
73
  seatTwoFor: () => seatTwoFor,
@@ -99,13 +102,12 @@ function usePackGame(id) {
99
102
  var import_auto_paper2 = require("@rific/auto-paper");
100
103
  var import_feedback_press2 = require("@rific/feedback-press");
101
104
  var import_toaster2 = require("@rific/toaster");
102
- var import_core6 = require("@tastic/core");
105
+ var import_core4 = require("@tastic/core");
103
106
  var import_chrome2 = require("@tastic/hud/chrome");
104
107
  var import_profile3 = require("@tastic/profile");
105
108
  var Haptics = __toESM(require("expo-haptics"));
106
109
  var import_expo_router = require("expo-router");
107
- var import_expo_sensors = require("expo-sensors");
108
- var import_react9 = require("react");
110
+ var import_react8 = require("react");
109
111
  var import_react_native2 = require("react-native");
110
112
  var import_react_native_gesture_handler = require("react-native-gesture-handler");
111
113
  var RNPaper2 = __toESM(require("react-native-paper"));
@@ -163,7 +165,6 @@ function GuideHost({ children }) {
163
165
  const packGuide = subject === PACK_GUIDE ? pack.guide : void 0;
164
166
  const game = subject === PACK_GUIDE ? void 0 : pack.games.find((g) => g.id === subject);
165
167
  const gameGuide = game?.guide;
166
- const rotation = (0, import_core.useRotation)();
167
168
  const touch = (0, import_core.useIsTouchPrimaryDevice)();
168
169
  const seatTwo = (0, import_react_redux.useSelector)((state) => state.opponent.seatTwo);
169
170
  const input = (0, import_react_redux.useSelector)((state) => gameGuide ? gameGuide.selectInput(state) : NO_INPUT, import_react_redux.shallowEqual);
@@ -178,7 +179,7 @@ function GuideHost({ children }) {
178
179
  },
179
180
  [dispatch, packGuide, gameGuide]
180
181
  );
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 }) });
182
+ 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
183
  }
183
184
  function GameGuideBridge({ onPick, children }) {
184
185
  const pack = usePack();
@@ -301,66 +302,33 @@ function Theme({ children }) {
301
302
 
302
303
  // src/components/UpdateDialog.tsx
303
304
  var import_toaster = require("@rific/toaster");
304
- var import_core3 = require("@tastic/core");
305
305
  var import_chrome = require("@tastic/hud/chrome");
306
306
  var import_jsx_runtime6 = require("react/jsx-runtime");
307
307
  function UpdateDialog() {
308
308
  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 });
309
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_chrome.UpdateDialog, { onError });
311
310
  }
312
311
 
313
- // src/hooks/useAppSettings.ts
312
+ // src/hooks/useLastActiveGame.tsx
314
313
  var import_react6 = require("react");
315
314
  var import_react_redux4 = require("react-redux");
316
315
 
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
316
  // 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;
317
+ var import_core3 = require("@rific/core");
318
+ var { actions, reducer } = (0, import_core3.createSettingsSlice)("lastActiveGame", { initialState: { gameId: null } });
319
+ var lastActiveGameActions = actions;
320
+ var lastActiveGameSlice_default = reducer;
353
321
 
354
322
  // src/hooks/useLastActiveGame.tsx
355
323
  var import_jsx_runtime7 = require("react/jsx-runtime");
356
- var LastActiveGameContext = (0, import_react7.createContext)(null);
324
+ var LastActiveGameContext = (0, import_react6.createContext)(null);
357
325
  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)(
326
+ const store = (0, import_react_redux4.useStore)();
327
+ const dispatch = (0, import_react_redux4.useDispatch)();
328
+ const [initial] = (0, import_react6.useState)(() => store.getState().lastActiveGame?.gameId ?? null);
329
+ const pendingResume = (0, import_react6.useRef)(initial);
330
+ const persisted = (0, import_react6.useRef)(initial);
331
+ const value = (0, import_react6.useMemo)(
364
332
  () => ({
365
333
  consumeResumeGame: () => {
366
334
  const game = pendingResume.current;
@@ -388,33 +356,33 @@ function LastActiveGameProvider({ children }) {
388
356
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LastActiveGameContext.Provider, { value, children });
389
357
  }
390
358
  function useLastActiveGame() {
391
- const ctx = (0, import_react7.useContext)(LastActiveGameContext);
359
+ const ctx = (0, import_react6.useContext)(LastActiveGameContext);
392
360
  if (!ctx) throw new Error("useLastActiveGame must be used within a LastActiveGameProvider");
393
361
  return ctx;
394
362
  }
395
363
 
396
364
  // src/hooks/useProfiles.tsx
397
365
  var import_profile2 = require("@tastic/profile");
398
- var import_react8 = require("react");
399
- var import_react_redux6 = require("react-redux");
366
+ var import_react7 = require("react");
367
+ var import_react_redux5 = require("react-redux");
400
368
 
401
369
  // src/redux/profileSelectionSlice.ts
402
370
  var import_profile = require("@tastic/profile");
403
371
  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;
372
+ var { actions: actions2, reducer: reducer2 } = (0, import_profile.createProfileSelectionSlice)("pack", defaultProfileSelectionState);
373
+ var profileSelectionActions = actions2;
374
+ var profileSelectionSlice_default = reducer2;
407
375
 
408
376
  // src/hooks/useProfiles.tsx
409
377
  var import_jsx_runtime8 = require("react/jsx-runtime");
410
378
  var SHARED_GROUP_ID = "group.com.infinitetoken.tastic";
411
- var ProfilesContext = (0, import_react8.createContext)(null);
379
+ var ProfilesContext = (0, import_react7.createContext)(null);
412
380
  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)(() => {
381
+ const profiles = (0, import_react_redux5.useSelector)((state) => state.profiles);
382
+ const lastSelected = (0, import_react_redux5.useSelector)((state) => state.profileSelection);
383
+ const dispatch = (0, import_react_redux5.useDispatch)();
384
+ const [loaded, setLoaded] = (0, import_react7.useState)(false);
385
+ (0, import_react7.useEffect)(() => {
418
386
  let cancelled = false;
419
387
  (0, import_profile2.resolveInitialProfiles)(SHARED_GROUP_ID, profiles).then((resolved) => {
420
388
  if (cancelled) return;
@@ -428,7 +396,7 @@ function ProfilesProvider({ children }) {
428
396
  }, []);
429
397
  const { syncToShared } = (0, import_profile2.useSharedProfilesSync)({
430
398
  groupId: SHARED_GROUP_ID,
431
- onRemoteChange: (0, import_react8.useCallback)(
399
+ onRemoteChange: (0, import_react7.useCallback)(
432
400
  (remote) => {
433
401
  dispatch(import_profile2.profilesActions.setAll(remote));
434
402
  dispatch(profileSelectionActions.pruneToRoster(remote.map((p) => p.id)));
@@ -436,7 +404,7 @@ function ProfilesProvider({ children }) {
436
404
  [dispatch]
437
405
  )
438
406
  });
439
- const createProfile = (0, import_react8.useCallback)(
407
+ const createProfile = (0, import_react7.useCallback)(
440
408
  (input) => {
441
409
  const record = (0, import_profile2.createProfileRecord)(input);
442
410
  dispatch(import_profile2.profilesActions.add(record));
@@ -445,14 +413,14 @@ function ProfilesProvider({ children }) {
445
413
  },
446
414
  [profiles, dispatch, syncToShared]
447
415
  );
448
- const updateProfile = (0, import_react8.useCallback)(
416
+ const updateProfile = (0, import_react7.useCallback)(
449
417
  (id, patch) => {
450
418
  dispatch(import_profile2.profilesActions.update({ id, patch }));
451
419
  syncToShared(profiles.map((p) => p.id === id ? { ...p, ...patch, updatedAt: Date.now() } : p));
452
420
  },
453
421
  [profiles, dispatch, syncToShared]
454
422
  );
455
- const deleteProfile = (0, import_react8.useCallback)(
423
+ const deleteProfile = (0, import_react7.useCallback)(
456
424
  (id) => {
457
425
  dispatch(import_profile2.profilesActions.remove(id));
458
426
  syncToShared(profiles.filter((p) => p.id !== id));
@@ -460,7 +428,7 @@ function ProfilesProvider({ children }) {
460
428
  },
461
429
  [profiles, dispatch, syncToShared]
462
430
  );
463
- const selectProfile = (0, import_react8.useCallback)(
431
+ const selectProfile = (0, import_react7.useCallback)(
464
432
  (seat, profileId) => {
465
433
  dispatch(profileSelectionActions.select({ profileId, seat }));
466
434
  },
@@ -469,7 +437,7 @@ function ProfilesProvider({ children }) {
469
437
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ProfilesContext.Provider, { value: { profiles, lastSelected, loaded, createProfile, updateProfile, deleteProfile, selectProfile }, children });
470
438
  }
471
439
  function useProfiles() {
472
- const ctx = (0, import_react8.useContext)(ProfilesContext);
440
+ const ctx = (0, import_react7.useContext)(ProfilesContext);
473
441
  if (!ctx) throw new Error("useProfiles must be used within a ProfilesProvider");
474
442
  return ctx;
475
443
  }
@@ -485,11 +453,11 @@ function AppStack() {
485
453
  const { Backdrop } = pack;
486
454
  const { dark } = (0, import_auto_paper2.useAutoPaperTheme)();
487
455
  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" });
456
+ const rootBackground = (0, import_core4.useThemedRootBackground)(darkRoot, false);
457
+ const statusBarStyle = darkRoot ? "light" : "dark";
490
458
  const packOptions = (pack.useScreenOptions ?? noScreenOptions)();
491
459
  const backdropOptions = Backdrop ? { contentStyle: TRANSPARENT_CONTENT, animation: import_react_native2.Platform.select({ android: "fade" }) } : void 0;
492
- const playOptions = (0, import_react9.useCallback)(
460
+ const playOptions = (0, import_react8.useCallback)(
493
461
  ({ route }) => {
494
462
  const id = route.params?.game;
495
463
  return pack.games.some((g) => g.id === id && g.keepStatusBar) ? KEEP_STATUS_BAR_OPTIONS : PLAY_OPTIONS;
@@ -497,11 +465,11 @@ function AppStack() {
497
465
  [pack.games]
498
466
  );
499
467
  const navigationBackground = Backdrop ? TRANSPARENT_CONTENT.backgroundColor : rootBackground.contentStyle.backgroundColor;
500
- const navigationTheme = (0, import_react9.useMemo)(() => {
468
+ const navigationTheme = (0, import_react8.useMemo)(() => {
501
469
  const base = darkRoot ? import_expo_router.DarkTheme : import_expo_router.DefaultTheme;
502
470
  return { ...base, colors: { ...base.colors, background: navigationBackground } };
503
471
  }, [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: [
472
+ 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
473
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_expo_router.Stack.Screen, { name: "index" }),
506
474
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_expo_router.Stack.Screen, { name: "play/[game]", options: playOptions })
507
475
  ] }) });
@@ -511,11 +479,6 @@ function AppStack() {
511
479
  stack
512
480
  ] });
513
481
  }
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
482
  function GatedApp({ children }) {
520
483
  const { loaded } = useProfiles();
521
484
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SplashGate, { gate: "profiles", ready: loaded, children });
@@ -526,7 +489,7 @@ function HapticGatedToastProvider({ children }) {
526
489
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_toaster2.ToastProvider, { haptics: settings.vibrate ? Haptics : void 0, paper: PackToasterComponent ? void 0 : RNPaper2, children });
527
490
  }
528
491
  function RotatingToaster() {
529
- const renderFrame = (0, import_react9.useCallback)(
492
+ const renderFrame = (0, import_react8.useCallback)(
530
493
  (toasts) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_chrome2.RotatedFrame, { rotatedSafeArea: true, passThrough: true, children: toasts }),
531
494
  []
532
495
  );
@@ -540,7 +503,7 @@ function GameProviders({ games, children }) {
540
503
  }
541
504
  function PreviewColors({ previewColor, children }) {
542
505
  const { dark } = (0, import_auto_paper2.useAutoPaperTheme)();
543
- const preview = (0, import_react9.useCallback)((hex) => previewColor(hex, dark), [previewColor, dark]);
506
+ const preview = (0, import_react8.useCallback)((hex) => previewColor(hex, dark), [previewColor, dark]);
544
507
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_profile3.ProfileColorProvider, { preview, children });
545
508
  }
546
509
  function PackRoot({ pack, children }) {
@@ -549,11 +512,11 @@ function PackRoot({ pack, children }) {
549
512
  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
513
  }
551
514
  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: [
515
+ 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
516
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(GuideHost, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AppStack, {}) }),
554
517
  pack.Toaster ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PackToaster, { Toaster: pack.Toaster }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RotatingToaster, {}),
555
518
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(UpdateDialog, {})
556
- ] }) }) }) }) }) }) }) }) }) }) }) }) });
519
+ ] }) }) }) }) }) }) }) }) }) }) }) });
557
520
  }
558
521
  var styles = import_react_native2.StyleSheet.create({
559
522
  root: { flex: 1 }
@@ -572,7 +535,7 @@ var PROFILES_HREF = "/profiles";
572
535
 
573
536
  // src/screens/LoadoutScreen.tsx
574
537
  var import_auto_paper4 = require("@rific/auto-paper");
575
- var import_core10 = require("@tastic/core");
538
+ var import_core9 = require("@tastic/core");
576
539
  var import_chrome4 = require("@tastic/hud/chrome");
577
540
  var import_lobby2 = require("@tastic/hud/lobby");
578
541
  var import_split_screen = require("@tastic/split-screen");
@@ -586,7 +549,7 @@ var import_react_redux9 = require("react-redux");
586
549
  // src/components/SeatTwoPanel.tsx
587
550
  var import_lobby = require("@tastic/hud/lobby");
588
551
  var import_profile4 = require("@tastic/profile");
589
- var import_react10 = require("react");
552
+ var import_react9 = require("react");
590
553
  var import_react_native3 = require("react-native");
591
554
  var import_jsx_runtime10 = require("react/jsx-runtime");
592
555
  var CPU_PREFIX = "cpu:";
@@ -604,7 +567,7 @@ function SeatTwoPanel({ idPrefix, host, choices, seatTwo, onSeatTwoChange, cpuDi
604
567
  const drawn = previewValue ? previewValue(color) : color;
605
568
  const selectedProfile = isPerson ? profiles.find((p) => p.id === selectedProfileId) ?? null : null;
606
569
  const cpuOptions = cpuDifficulty?.options;
607
- const options = (0, import_react10.useMemo)(() => {
570
+ const options = (0, import_react9.useMemo)(() => {
608
571
  const list = [];
609
572
  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
573
  if (choices.includes("nobody")) list.push({ value: NOBODY, label: "Nobody", icon: NOBODY_ICON });
@@ -664,13 +627,45 @@ var styles2 = import_react_native3.StyleSheet.create({
664
627
  // src/components/SettingsDialog.tsx
665
628
  var import_toaster3 = require("@rific/toaster");
666
629
  var import_chrome3 = require("@tastic/hud/chrome");
630
+
631
+ // src/hooks/useAppSettings.ts
632
+ var import_react10 = require("react");
633
+ var import_react_redux6 = require("react-redux");
634
+
635
+ // src/redux/appSettingsSlice.ts
636
+ var import_core5 = require("@rific/core");
637
+ var DEFAULT_APP_SETTINGS = {
638
+ deferBottomEdgeGestures: false
639
+ };
640
+ var { actions: actions3, reducer: reducer3 } = (0, import_core5.createSettingsSlice)("appSettings", {
641
+ initialState: DEFAULT_APP_SETTINGS,
642
+ initializeMode: "merge",
643
+ fieldSetters: []
644
+ });
645
+ var appSettingsActions = actions3;
646
+ var appSettingsSlice_default = reducer3;
647
+
648
+ // src/hooks/useAppSettings.ts
649
+ function useAppSettings() {
650
+ const settings = (0, import_react_redux6.useSelector)((state) => state.appSettings);
651
+ const dispatch = (0, import_react_redux6.useDispatch)();
652
+ const setSettings = (0, import_react10.useCallback)(
653
+ (patch) => {
654
+ dispatch(appSettingsActions.initialize(patch));
655
+ },
656
+ [dispatch]
657
+ );
658
+ return { settings, setSettings };
659
+ }
660
+
661
+ // src/components/SettingsDialog.tsx
667
662
  var import_jsx_runtime11 = require("react/jsx-runtime");
668
- function SettingsDialog({ visible, onDismiss, rotation = 0, onShowHowToPlay, children }) {
663
+ function SettingsDialog({ visible, onDismiss, onShowHowToPlay, children }) {
669
664
  const onUpdateError = (0, import_toaster3.useUpdateErrorToast)();
670
665
  const { settings, setSettings } = useAppSettings();
671
666
  const { otaVersion, SettingsSections, edgeGuard = true } = usePack();
672
667
  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: [
668
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_chrome3.BaseSettingsDialog, { visible, onDismiss, version: otaVersion, ...edgeGuardProps, onUpdateError, onShowHowToPlay, children: [
674
669
  children,
675
670
  SettingsSections && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SettingsSections, {})
676
671
  ] });
@@ -681,9 +676,9 @@ var import_react11 = require("react");
681
676
  var import_react_redux7 = require("react-redux");
682
677
 
683
678
  // src/redux/opponentSlice.ts
684
- var import_core7 = require("@rific/core");
679
+ var import_core6 = require("@rific/core");
685
680
  var DEFAULT_OPPONENT = { seatTwo: "cpu" };
686
- var { actions: actions4, reducer: reducer4 } = (0, import_core7.createSettingsSlice)("opponent", {
681
+ var { actions: actions4, reducer: reducer4 } = (0, import_core6.createSettingsSlice)("opponent", {
687
682
  initialState: DEFAULT_OPPONENT
688
683
  });
689
684
  var opponentActions = actions4;
@@ -816,13 +811,13 @@ function useSeatColors(p2IsHuman) {
816
811
  }
817
812
 
818
813
  // src/navigation.ts
819
- var import_core8 = require("@rific/core");
814
+ var import_core7 = require("@rific/core");
820
815
  var import_expo_router3 = require("expo-router");
821
- (0, import_core8.configureNavigation)({ router: import_expo_router3.router });
816
+ (0, import_core7.configureNavigation)({ router: import_expo_router3.router });
822
817
 
823
818
  // src/redux/liveplaySlice.ts
824
- var import_core9 = require("@rific/core");
825
- var { actions: actions5, reducer: reducer5 } = (0, import_core9.createSettingsSlice)("liveplay", {
819
+ var import_core8 = require("@rific/core");
820
+ var { actions: actions5, reducer: reducer5 } = (0, import_core8.createSettingsSlice)("liveplay", {
826
821
  initialState: { activelyPlaying: false, activeRound: null }
827
822
  });
828
823
  var liveplayActions = actions5;
@@ -842,14 +837,12 @@ function LoadoutScreen({ game }) {
842
837
  const loadout = game.useLoadout(mode);
843
838
  const { openFor } = useGameGuide();
844
839
  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);
840
+ const { orientationMode } = (0, import_core9.useOrientationState)();
841
+ const panelMode = p2IsHuman && !game.sideBySideWhenWide ? "faceToFace" : orientationMode;
842
+ const { panelLayout, panelFadeStyle } = (0, import_split_screen.useDualZoneLayout)(panelMode, false, true, false, PANEL_SWAP_FADE_MS);
849
843
  const playersRowFade = panelFadeStyle;
850
844
  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);
845
+ const insets = (0, import_react_native_safe_area_context2.useSafeAreaInsets)();
853
846
  const { colors: themeColors, dark } = (0, import_auto_paper4.useAutoPaperTheme)();
854
847
  const { p1Color, p2Color, handleP1ColorChange, handleP2ColorChange } = useSeatColors(p2IsHuman);
855
848
  const dispatch = (0, import_react_redux9.useDispatch)();
@@ -887,7 +880,7 @@ function LoadoutScreen({ game }) {
887
880
  const triggerStyle = (0, import_react13.useMemo)(() => pack.Backdrop ? [styles3.pill, { backgroundColor: themeColors.surface }] : void 0, [pack.Backdrop, themeColors.surface]);
888
881
  const isPortrait = panelLayout.orientationMode === "faceToFace";
889
882
  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;
883
+ const p2ZoneStyle = p2IsHuman ? isPortrait ? styles3.p2ZoneTop : styles3.p2ZoneRight : null;
891
884
  const showReadyButton = p2IsHuman;
892
885
  const seatPickerAlign = p2IsHuman ? void 0 : pack.usePickerAlign;
893
886
  const layoutKey = p2IsHuman ? "zones" : "row";
@@ -897,7 +890,6 @@ function LoadoutScreen({ game }) {
897
890
  const anyPlayerPickerOpen = !!(controlsHost.openId?.startsWith("p1-") || controlsHost.openId?.startsWith("p2-"));
898
891
  const optionsRow = loadout.options({
899
892
  host: controlsHost,
900
- rotation,
901
893
  accentColor: themeColors.tertiary,
902
894
  mutedColor: fgMuted,
903
895
  onAccentColor: themeColors.onTertiary,
@@ -907,17 +899,17 @@ function LoadoutScreen({ game }) {
907
899
  onReset: showMetaInSharedBand ? void 0 : loadout.reset
908
900
  });
909
901
  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 };
902
+ 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;
903
+ const controlLook = { accentColor: themeColors.tertiary, mutedColor: fgMuted, dark };
912
904
  const p1Control = loadout.seatControl(1, controlsHost, { ...controlLook, seatColor: p1Drawn });
913
905
  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
906
  const p2Control = p2IsHuman ? loadout.seatControl(2, p2Host, { ...controlLook, seatColor: p2Drawn }) : void 0;
915
907
  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
908
  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: [
909
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native4.View, { style: styles3.frame, children: [
918
910
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_chrome4.PressAwayOverlay, { active: controlsHost.openId !== null, onPress: controlsHost.close }),
919
911
  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 }),
912
+ !showMetaInSharedBand && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_chrome4.CornerActionButtons, { onBack: import_core7.safeBack, onSettings: () => setSettingsOpen(true), fg, insets }),
921
913
  p2IsHuman ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
922
914
  import_split_screen.DualZoneLayout,
923
915
  {
@@ -933,13 +925,13 @@ function LoadoutScreen({ game }) {
933
925
  ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native4.View, { style: styles3.stackedZone, children: [
934
926
  sharedControls,
935
927
  /* @__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
928
+ p1Panel,
929
+ p2Panel
938
930
  ] }),
939
931
  /* @__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
932
  ] })
941
933
  ] }),
942
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false), rotation, onShowHowToPlay: () => openFor(game.id) })
934
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false), onShowHowToPlay: () => openFor(game.id) })
943
935
  ] });
944
936
  }
945
937
  function halfOf(color) {
@@ -950,12 +942,13 @@ var styles3 = import_react_native4.StyleSheet.create({
950
942
  container: {
951
943
  flex: 1
952
944
  },
953
- p2ZoneLeft: {
954
- bottom: 0,
955
- left: 0,
956
- position: "absolute",
957
- top: 0,
958
- width: "50%"
945
+ // A real full-bleed box to size the absolutely-positioned children against.
946
+ frame: {
947
+ alignItems: "center",
948
+ flex: 1,
949
+ gap: 32,
950
+ justifyContent: "center",
951
+ paddingHorizontal: 16
959
952
  },
960
953
  p2ZoneRight: {
961
954
  bottom: 0,
@@ -990,14 +983,6 @@ var styles3 = import_react_native4.StyleSheet.create({
990
983
  opacity: 0,
991
984
  pointerEvents: "none"
992
985
  },
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
986
  stackedZone: {
1002
987
  alignItems: "center",
1003
988
  gap: 28
@@ -1038,12 +1023,11 @@ function useResumePoint(gameId) {
1038
1023
  // src/screens/HomeScreen.tsx
1039
1024
  var import_auto_paper5 = require("@rific/auto-paper");
1040
1025
  var import_feedback_press3 = require("@rific/feedback-press");
1041
- var import_core12 = require("@tastic/core");
1042
1026
  var import_chrome5 = require("@tastic/hud/chrome");
1043
1027
  var import_profile7 = require("@tastic/profile");
1044
1028
  var import_expo_router6 = require("expo-router");
1045
1029
  var import_react16 = require("react");
1046
- var import_react_native6 = require("react-native");
1030
+ var import_react_native7 = require("react-native");
1047
1031
  var import_react_native_paper = require("react-native-paper");
1048
1032
 
1049
1033
  // src/hooks/useFrozenWhileHidden.ts
@@ -1063,7 +1047,8 @@ function useFrozenWhileHidden(value, hidden) {
1063
1047
  }
1064
1048
 
1065
1049
  // src/screens/homeLayout.ts
1066
- var import_core11 = require("@tastic/core");
1050
+ var import_core10 = require("@tastic/core");
1051
+ var import_react_native6 = require("react-native");
1067
1052
  var import_react_native_safe_area_context3 = require("react-native-safe-area-context");
1068
1053
  var PREVIEW_ASPECT = 360 / 200;
1069
1054
  var ROW_PREVIEW_WIDTH = 92;
@@ -1092,9 +1077,9 @@ function computeHomeLayout(windowWidth, windowHeight, insets) {
1092
1077
  const inner = Math.max(0, usable - HOME_WIDE_EDGE_PADDING - HOME_WIDE_RIGHT_GUTTER);
1093
1078
  const content = Math.min(inner, HOME_WIDE_MAX_CONTENT_WIDTH);
1094
1079
  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));
1080
+ 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
1081
  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);
1082
+ const columns = (0, import_core10.clamp)(Math.floor((rightPaneWidth + HOME_GRID_GAP) / (HOME_MIN_TILE_WIDTH + HOME_GRID_GAP)), 1, HOME_MAX_COLUMNS);
1098
1083
  const tileWidth = Math.max(0, Math.floor((rightPaneWidth - (columns - 1) * HOME_GRID_GAP) / columns));
1099
1084
  const previewWidth = Math.max(0, tileWidth - 2 * HOME_TILE_PADDING);
1100
1085
  const heroFit = {
@@ -1117,9 +1102,8 @@ function computeHomeLayout(windowWidth, windowHeight, insets) {
1117
1102
  };
1118
1103
  }
1119
1104
  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);
1105
+ const { width, height } = (0, import_react_native6.useWindowDimensions)();
1106
+ const insets = (0, import_react_native_safe_area_context3.useSafeAreaInsets)();
1123
1107
  return computeHomeLayout(width, height, insets);
1124
1108
  }
1125
1109
 
@@ -1141,7 +1125,6 @@ function HomeScreen() {
1141
1125
  }
1142
1126
  function HomeMenu() {
1143
1127
  const pack = usePack();
1144
- const rotation = (0, import_core12.useRotation)();
1145
1128
  const layout = useHomeLayout();
1146
1129
  const { insets } = layout;
1147
1130
  const { dark, colors } = (0, import_auto_paper5.useAutoPaperTheme)();
@@ -1149,9 +1132,9 @@ function HomeMenu() {
1149
1132
  const [settingsOpen, setSettingsOpen] = (0, import_react16.useState)(false);
1150
1133
  const [rulesGame, setRulesGame] = (0, import_react16.useState)(null);
1151
1134
  const isFocused = (0, import_expo_router6.useIsFocused)();
1152
- const [appState, setAppState] = (0, import_react16.useState)(import_react_native6.AppState.currentState);
1135
+ const [appState, setAppState] = (0, import_react16.useState)(import_react_native7.AppState.currentState);
1153
1136
  (0, import_react16.useEffect)(() => {
1154
- const subscription = import_react_native6.AppState.addEventListener("change", setAppState);
1137
+ const subscription = import_react_native7.AppState.addEventListener("change", setAppState);
1155
1138
  return () => subscription.remove();
1156
1139
  }, []);
1157
1140
  const heroActive = isFocused && appState === "active";
@@ -1171,7 +1154,7 @@ function HomeMenu() {
1171
1154
  const closePicker = pickerHost.close;
1172
1155
  (0, import_react16.useEffect)(() => {
1173
1156
  closePicker();
1174
- }, [isWide, rotation, layout.windowWidth, layout.windowHeight, closePicker]);
1157
+ }, [isWide, layout.windowWidth, layout.windowHeight, closePicker]);
1175
1158
  const bg = pack.Backdrop ? "transparent" : dark ? "#000000" : "#FFFFFF";
1176
1159
  const fg = pack.foreground ?? (dark ? "#FFFFFF" : "#000000");
1177
1160
  const { previewColor } = pack;
@@ -1193,30 +1176,30 @@ function HomeMenu() {
1193
1176
  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 });
1194
1177
  const pressAway = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_chrome5.PressAwayOverlay, { active: pickerHost.openId !== null, onPress: pickerHost.close, style: styles4.pressAway });
1195
1178
  const RulesSheet = rulesGame?.Rules;
1196
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { style: [styles4.container, { backgroundColor: bg }], children: [
1197
- /* @__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: [
1179
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native7.View, { style: [styles4.container, { backgroundColor: bg }], children: [
1180
+ /* @__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: [
1198
1181
  isWide ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
1199
1182
  pressAway,
1200
1183
  /* @__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: [
1201
- tabBar && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.wideTabs, { width: layout.rightPaneWidth }], children: tabBar }),
1202
- /* @__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, 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)) })
1184
+ tabBar && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: [styles4.wideTabs, { width: layout.rightPaneWidth }], children: tabBar }),
1185
+ /* @__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)) })
1203
1186
  ] }),
1204
- /* @__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: [
1187
+ /* @__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: [
1205
1188
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(pack.HeroTitle, { words: pack.wordmark, color: fg, p1Color, p2Color, active: heroActive, fit: layout.heroFit }),
1206
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: styles4.widePicker, children: picker })
1189
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: styles4.widePicker, children: picker })
1207
1190
  ] })
1208
1191
  ] }) : /* @__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: [
1209
1192
  pressAway,
1210
1193
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(pack.HeroTitle, { words: pack.wordmark, color: fg, p1Color, p2Color, active: heroActive }),
1211
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.picker, pickerHost.openId !== null && styles4.pickerOpen], children: picker }),
1212
- tabBar && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: styles4.listWidth, children: tabBar }),
1213
- /* @__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, hidden: isHidden(tab, game), dark, p1Color, p2Color, previewRevealed: previewsRevealed, onPress: () => openGame(game), onRules: hasRules(game) ? () => openRules(game) : void 0 }, game.id)) })
1194
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: [styles4.picker, pickerHost.openId !== null && styles4.pickerOpen], children: picker }),
1195
+ tabBar && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native7.View, { style: styles4.listWidth, children: tabBar }),
1196
+ /* @__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)) })
1214
1197
  ] }),
1215
1198
  /* @__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" }),
1216
1199
  /* @__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" }),
1217
1200
  RulesSheet && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(RulesSheet, { visible: true, onDismiss: () => setRulesGame(null) })
1218
1201
  ] }) }),
1219
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false), rotation, onShowHowToPlay: pack.guide ? openPack : void 0 })
1202
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false), onShowHowToPlay: pack.guide ? openPack : void 0 })
1220
1203
  ] });
1221
1204
  }
1222
1205
  var SEAT_ONE_PICKER = "home-p1";
@@ -1261,10 +1244,10 @@ function GameRow({ game, hidden, dark, p1Color, p2Color, previewRevealed, onPres
1261
1244
  return (
1262
1245
  // The rules button sits beside the row's own button, laid over its end, not inside it: a button
1263
1246
  // inside a button is invalid on the web.
1264
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { style: hidden ? styles4.hidden : void 0, children: [
1247
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native7.View, { style: hidden ? styles4.hidden : void 0, children: [
1265
1248
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_feedback_press3.Pressable, { onPress, style: [styles4.row, { backgroundColor: well.bg }], accessibilityRole: "button", accessibilityLabel: game.title, children: [
1266
- 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 }) }),
1267
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { style: styles4.rowText, children: [
1249
+ 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 }) }),
1250
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native7.View, { style: styles4.rowText, children: [
1268
1251
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper.Text, { variant: "titleLarge", style: [styles4.rowTitle, { color: well.fg }], numberOfLines: 1, children: game.title }),
1269
1252
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper.Text, { variant: "bodyMedium", style: { color: well.fgMuted }, numberOfLines: 2, children: game.blurb })
1270
1253
  ] })
@@ -1275,7 +1258,7 @@ function GameRow({ game, hidden, dark, p1Color, p2Color, previewRevealed, onPres
1275
1258
  }
1276
1259
  function GameTile({ game, hidden, width, previewWidth, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
1277
1260
  const well = (0, import_chrome5.useWellColors)();
1278
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { style: [{ width }, hidden && styles4.hidden], children: [
1261
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native7.View, { style: [{ width }, hidden && styles4.hidden], children: [
1279
1262
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_feedback_press3.Pressable, { onPress, style: [styles4.tile, { backgroundColor: well.bg }], accessibilityRole: "button", accessibilityLabel: game.title, children: [
1280
1263
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PreviewBox, { game, width: previewWidth, revealed: previewRevealed, dark, p1Color, p2Color }),
1281
1264
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper.Text, { variant: "titleMedium", style: [styles4.rowTitle, { color: well.fg }], numberOfLines: 1, children: game.title }),
@@ -1288,13 +1271,13 @@ function PreviewBox({ game, width, revealed, dark, p1Color, p2Color }) {
1288
1271
  const height = Math.round(width / PREVIEW_ASPECT);
1289
1272
  const { Preview, icon: GameIcon } = game;
1290
1273
  const iconSize = Math.min(height, ROW_ICON_SIZE);
1291
- 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 }) }) });
1274
+ 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 }) }) });
1292
1275
  }
1293
1276
  var ROW_ICON_SIZE = 56;
1294
1277
  var ICON_LIGHT_BG = "#FAFAFA";
1295
1278
  var ICON_DARK_BG = "#050505";
1296
1279
  var RULES_BUTTON_SPACE = 44;
1297
- var styles4 = import_react_native6.StyleSheet.create({
1280
+ var styles4 = import_react_native7.StyleSheet.create({
1298
1281
  container: {
1299
1282
  flex: 1
1300
1283
  },
@@ -1305,6 +1288,9 @@ var styles4 = import_react_native6.StyleSheet.create({
1305
1288
  corner: {
1306
1289
  position: "absolute"
1307
1290
  },
1291
+ frame: {
1292
+ flex: 1
1293
+ },
1308
1294
  // The wide layout's tiles: wrapped rows of tiles the layout sizes to fill the pane's width.
1309
1295
  grid: {
1310
1296
  flexDirection: "row",
@@ -1351,9 +1337,6 @@ var styles4 = import_react_native6.StyleSheet.create({
1351
1337
  previewIcon: {
1352
1338
  overflow: "hidden"
1353
1339
  },
1354
- rotatable: {
1355
- flex: 1
1356
- },
1357
1340
  // A well (@tastic/hud's wellSurface): the theme's surface on MD3's level-1 shadow, the same surface
1358
1341
  // as Solitaire's game rows, the stats screen's sections and every dialog.
1359
1342
  row: {
@@ -1450,7 +1433,7 @@ function useRosterAlign(open, contentWidth, contentHeight) {
1450
1433
  function ProfilesRoute() {
1451
1434
  const { profiles, createProfile, updateProfile, deleteProfile } = useProfiles();
1452
1435
  const { Backdrop, foreground, newProfileColor, usePickerAlign } = usePack();
1453
- 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 }) });
1436
+ 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 }) });
1454
1437
  }
1455
1438
 
1456
1439
  // src/screens/StatsScreen.tsx
@@ -1458,7 +1441,7 @@ var import_auto_paper7 = require("@rific/auto-paper");
1458
1441
  var import_chrome7 = require("@tastic/hud/chrome");
1459
1442
  var import_profile9 = require("@tastic/profile");
1460
1443
  var import_react17 = require("react");
1461
- var import_react_native7 = require("react-native");
1444
+ var import_react_native8 = require("react-native");
1462
1445
  var import_react_native_paper2 = require("react-native-paper");
1463
1446
  var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
1464
1447
  var import_react_redux10 = require("react-redux");
@@ -1481,7 +1464,7 @@ function StatsScreen() {
1481
1464
  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 }) }) });
1482
1465
  }
1483
1466
  function StatsPressAway({ host, children }) {
1484
- 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 });
1467
+ 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 });
1485
1468
  }
1486
1469
  var useNoPackReset = () => null;
1487
1470
  var noPickerAlign2 = () => void 0;
@@ -1515,10 +1498,10 @@ function StatsContent({ pickerHost }) {
1515
1498
  const surface = pack.Backdrop ? { backgroundColor: colors.surface } : void 0;
1516
1499
  const triggerStyle = pack.Backdrop ? [styles5.pill, surface] : void 0;
1517
1500
  const PackTabContent = packTab?.Content;
1518
- 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: [
1501
+ 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: [
1519
1502
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_chrome7.PressAwayOverlay, { active: pickerHost.openId !== null, onPress: pickerHost.close, style: styles5.pressAway }),
1520
1503
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper2.SegmentedButtons, { value: tab, onValueChange: setTab, buttons: tabButtons, style: [styles5.tabs, surface] }),
1521
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native7.View, { style: [styles5.filters, pickerHost.openId !== null && styles5.filtersOpen], onStartShouldSetResponder: () => pickerHost.openId !== null, children: [
1504
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native8.View, { style: [styles5.filters, pickerHost.openId !== null && styles5.filtersOpen], onStartShouldSetResponder: () => pickerHost.openId !== null, children: [
1522
1505
  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 }),
1523
1506
  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 })
1524
1507
  ] }),
@@ -1539,12 +1522,12 @@ function GameStats({ game, tab, profileId, showTitle, titleColor }) {
1539
1522
  const { StatsSections, AchievementList } = game;
1540
1523
  const Sections = tab === STATS ? StatsSections : AchievementList;
1541
1524
  if (!Sections) return null;
1542
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native7.View, { style: styles5.game, children: [
1525
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native8.View, { style: styles5.game, children: [
1543
1526
  showTitle && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper2.Text, { variant: "titleLarge", style: [styles5.gameTitle, { color: titleColor }], children: game.title }),
1544
1527
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Sections, { profileId })
1545
1528
  ] });
1546
1529
  }
1547
- var styles5 = import_react_native7.StyleSheet.create({
1530
+ var styles5 = import_react_native8.StyleSheet.create({
1548
1531
  filters: {
1549
1532
  flexDirection: "row",
1550
1533
  flexWrap: "wrap",
@@ -1578,14 +1561,54 @@ var styles5 = import_react_native7.StyleSheet.create({
1578
1561
  });
1579
1562
 
1580
1563
  // src/components/imageGameIcon.tsx
1581
- var import_react_native8 = require("react-native");
1564
+ var import_react_native9 = require("react-native");
1582
1565
  var import_jsx_runtime17 = require("react/jsx-runtime");
1583
1566
  function imageGameIcon(images) {
1584
1567
  return function ImageGameIcon({ size, dark }) {
1585
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native8.Image, { source: dark ? images.dark : images.light, style: { height: size, width: size }, accessibilityIgnoresInvertColors: true });
1568
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native9.Image, { source: dark ? images.dark : images.light, style: { height: size, width: size }, accessibilityIgnoresInvertColors: true });
1586
1569
  };
1587
1570
  }
1588
1571
 
1572
+ // src/components/MatchChips.tsx
1573
+ var import_auto_paper8 = require("@rific/auto-paper");
1574
+ var import_feedback_press4 = require("@rific/feedback-press");
1575
+ var import_react18 = require("react");
1576
+ var import_react_native10 = require("react-native");
1577
+ var import_react_native_reanimated3 = __toESM(require("react-native-reanimated"));
1578
+ var import_react_native_safe_area_context5 = require("react-native-safe-area-context");
1579
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1580
+ var MATCH_CHIPS_DESKTOP_MIN_WIDTH = 768;
1581
+ var MATCH_CHIPS_FADE_MS = 150;
1582
+ var CHIP_SIZE = 48;
1583
+ var EDGE_PAD = 8;
1584
+ function MatchChips({ playing, faceToFace, centerLineY, onBack, onSettings }) {
1585
+ const { width, height } = (0, import_react_native10.useWindowDimensions)();
1586
+ const insets = (0, import_react_native_safe_area_context5.useSafeAreaInsets)();
1587
+ const { dark } = (0, import_auto_paper8.useAutoPaperTheme)();
1588
+ const desktopWeb = import_react_native10.Platform.OS === "web" && width >= MATCH_CHIPS_DESKTOP_MIN_WIDTH;
1589
+ const visible = desktopWeb || !playing;
1590
+ const opacity = (0, import_react_native_reanimated3.useSharedValue)(visible ? 1 : 0);
1591
+ (0, import_react18.useEffect)(() => {
1592
+ opacity.value = (0, import_react_native_reanimated3.withTiming)(visible ? 1 : 0, { duration: MATCH_CHIPS_FADE_MS });
1593
+ }, [visible]);
1594
+ const fadeStyle = (0, import_react_native_reanimated3.useAnimatedStyle)(() => ({ opacity: opacity.value }));
1595
+ const iconColor = dark ? "#FFFFFF" : "#000000";
1596
+ const containerColor = dark ? "rgba(0,0,0,0.55)" : "rgba(255,255,255,0.55)";
1597
+ const between = faceToFace && !desktopWeb;
1598
+ const top = between ? (centerLineY ?? height / 2) - CHIP_SIZE / 2 : EDGE_PAD + insets.top;
1599
+ const backSlot = { top, left: EDGE_PAD + insets.left };
1600
+ const settingsSlot = { top, right: EDGE_PAD + insets.right };
1601
+ const pointerEvents = visible ? "box-none" : "none";
1602
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
1603
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native_reanimated3.default.View, { pointerEvents, style: [styles6.slot, backSlot, fadeStyle], testID: "match-chips-back", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press4.IconButton, { icon: "arrow-left", iconColor, containerColor, style: styles6.chip, size: 24, onPress: onBack, accessibilityLabel: "Back" }) }),
1604
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native_reanimated3.default.View, { pointerEvents, style: [styles6.slot, settingsSlot, fadeStyle], testID: "match-chips-settings", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_feedback_press4.IconButton, { icon: "cog", iconColor, containerColor, style: styles6.chip, size: 24, onPress: onSettings, accessibilityLabel: "Settings" }) })
1605
+ ] });
1606
+ }
1607
+ var styles6 = import_react_native10.StyleSheet.create({
1608
+ chip: { margin: 0 },
1609
+ slot: { position: "absolute" }
1610
+ });
1611
+
1589
1612
  // src/hooks/useActiveRound.ts
1590
1613
  var import_react_redux11 = require("react-redux");
1591
1614
  function useActiveRound(game, defaults) {
@@ -1601,12 +1624,12 @@ var import_toolkit = require("@reduxjs/toolkit");
1601
1624
  var import_redux_persist = require("redux-persist");
1602
1625
 
1603
1626
  // src/redux/shellState.ts
1604
- var import_auto_paper8 = require("@rific/auto-paper");
1605
- var import_core13 = require("@rific/core");
1606
- var import_feedback_press4 = require("@rific/feedback-press");
1627
+ var import_auto_paper9 = require("@rific/auto-paper");
1628
+ var import_core11 = require("@rific/core");
1629
+ var import_feedback_press5 = require("@rific/feedback-press");
1607
1630
  var import_profile10 = require("@tastic/profile");
1608
- var initialSoundSettings = { ...import_feedback_press4.defaultSoundSettings, enabled: !__DEV__ };
1609
- var appSoundReducer = (state = initialSoundSettings, action) => (0, import_feedback_press4.soundReducer)(state, action);
1631
+ var initialSoundSettings = { ...import_feedback_press5.defaultSoundSettings, enabled: !__DEV__ };
1632
+ var appSoundReducer = (state = initialSoundSettings, action) => (0, import_feedback_press5.soundReducer)(state, action);
1610
1633
  function createShellReducers({ seatColors, appearance = "system", seatTheme }) {
1611
1634
  return {
1612
1635
  // Explicit primary/secondary triad (rather than a single seed expanded via harmony) so the pack's
@@ -1618,12 +1641,12 @@ function createShellReducers({ seatColors, appearance = "system", seatTheme }) {
1618
1641
  // createThemeReducer's own default, when it sets none).
1619
1642
  // A pack with its own seatTheme starts from what seat 1's default color makes of it instead (dark
1620
1643
  // only when the pack's appearance is; hooks/useSeatColors.ts re-applies it with the live mode).
1621
- theme: (0, import_auto_paper8.createThemeReducer)(seatTheme ? { appearance, ...seatTheme(seatColors, appearance === "dark") } : { appearance, color: { primary: seatColors[1], secondary: seatColors[2], tertiary: (0, import_auto_paper8.getThirdColor)(seatColors[1], seatColors[2]) } }),
1622
- haptic: import_feedback_press4.hapticReducer,
1644
+ theme: (0, import_auto_paper9.createThemeReducer)(seatTheme ? { appearance, ...seatTheme(seatColors, appearance === "dark") } : { appearance, color: { primary: seatColors[1], secondary: seatColors[2], tertiary: (0, import_auto_paper9.getThirdColor)(seatColors[1], seatColors[2]) } }),
1645
+ haptic: import_feedback_press5.hapticReducer,
1623
1646
  sound: appSoundReducer,
1624
1647
  // @rific/core's Keep Awake setting (see Providers.tsx's KeepAwakeBridge), under the namespace its
1625
1648
  // REHYDRATE handling expects.
1626
- keepAwake: import_core13.keepAwakeReducer,
1649
+ keepAwake: import_core11.keepAwakeReducer,
1627
1650
  // The shared App Group roster (see @tastic/profile's resolveInitialProfiles/
1628
1651
  // useSharedProfilesSync, wired in hooks/useProfiles.tsx) is layered on top of this as a separate
1629
1652
  // sync target - this slice persisting normally via redux-persist is what gives it local-fallback
@@ -1729,8 +1752,8 @@ function forgetProfileAchievements(dropStats) {
1729
1752
 
1730
1753
  // src/gameStats.tsx
1731
1754
  var import_achievements2 = require("@tastic/achievements");
1732
- var import_react18 = require("react");
1733
- var import_jsx_runtime18 = require("react/jsx-runtime");
1755
+ var import_react19 = require("react");
1756
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1734
1757
  var SEATS = [1, 2];
1735
1758
  function createGameStats({ key, defaultStats, isValidStats, catalog, getProfileStatsView }) {
1736
1759
  const { actions: actions6, reducer: sliceReducer } = (0, import_achievements2.createAchievementsSlice)({
@@ -1747,7 +1770,7 @@ function createGameStats({ key, defaultStats, isValidStats, catalog, getProfileS
1747
1770
  };
1748
1771
  const reducer6 = onProfileRemoved(sliceReducer, forgetProfileAchievements(dropProfileStats));
1749
1772
  const profileViews = (stats) => Object.fromEntries(Object.entries(stats.profiles).map(([profileId, bucket]) => [profileId, getProfileStatsView(bucket)]));
1750
- const Context = (0, import_react18.createContext)(null);
1773
+ const Context = (0, import_react19.createContext)(null);
1751
1774
  function Provider({ children }) {
1752
1775
  const { stats, unlockedAchievements, recordOutcome, resetAll } = (0, import_achievements2.useAchievements)({
1753
1776
  catalog,
@@ -1755,18 +1778,18 @@ function createGameStats({ key, defaultStats, isValidStats, catalog, getProfileS
1755
1778
  actions: actions6,
1756
1779
  profileViews
1757
1780
  });
1758
- const record = (0, import_react18.useCallback)(
1781
+ const record = (0, import_react19.useCallback)(
1759
1782
  (update, profileIds) => {
1760
1783
  const result = recordOutcome(update);
1761
1784
  return { device: result.device, profiles: (0, import_achievements2.mapUnlocksBySeat)(SEATS, profileIds, result.profiles) };
1762
1785
  },
1763
1786
  [recordOutcome]
1764
1787
  );
1765
- const value = (0, import_react18.useMemo)(() => ({ stats, unlockedAchievements, record, resetAll }), [stats, unlockedAchievements, record, resetAll]);
1766
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Context.Provider, { value, children });
1788
+ const value = (0, import_react19.useMemo)(() => ({ stats, unlockedAchievements, record, resetAll }), [stats, unlockedAchievements, record, resetAll]);
1789
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Context.Provider, { value, children });
1767
1790
  }
1768
1791
  function useStats() {
1769
- const ctx = (0, import_react18.useContext)(Context);
1792
+ const ctx = (0, import_react19.useContext)(Context);
1770
1793
  if (!ctx) throw new Error(`The ${key} stats are read within their game's Provider`);
1771
1794
  return ctx;
1772
1795
  }
@@ -1774,7 +1797,7 @@ function createGameStats({ key, defaultStats, isValidStats, catalog, getProfileS
1774
1797
  }
1775
1798
 
1776
1799
  // src/playerSettings.ts
1777
- var import_react19 = require("react");
1800
+ var import_react20 = require("react");
1778
1801
  var import_react_redux12 = require("react-redux");
1779
1802
  var SEATS2 = [1, 2];
1780
1803
  function seatPlayerKey(selection, seat) {
@@ -1818,8 +1841,8 @@ function createPlayerSettings({ id, defaults, distinctControls }) {
1818
1841
  const saved = (0, import_react_redux12.useSelector)((state) => state[settingsKey]);
1819
1842
  const controls = (0, import_react_redux12.useSelector)((state) => state[controlsKey]);
1820
1843
  const dispatch = (0, import_react_redux12.useDispatch)();
1821
- const settings = (0, import_react19.useMemo)(() => compose(saved, controls, selection, seatTwoIsPerson), [saved, controls, selection, seatTwoIsPerson]);
1822
- const setSettings = (0, import_react19.useCallback)(
1844
+ const settings = (0, import_react20.useMemo)(() => compose(saved, controls, selection, seatTwoIsPerson), [saved, controls, selection, seatTwoIsPerson]);
1845
+ const setSettings = (0, import_react20.useCallback)(
1823
1846
  (next) => {
1824
1847
  const { controlScheme, ...changes } = next;
1825
1848
  if (Object.keys(changes).length > 0) dispatch(patch(seatPlayerKey(selection, 1), changes));
@@ -1842,6 +1865,9 @@ function createPlayerSettings({ id, defaults, distinctControls }) {
1842
1865
  GUEST_PLAYER_ID,
1843
1866
  HomeScreen,
1844
1867
  LoadoutRoute,
1868
+ MATCH_CHIPS_DESKTOP_MIN_WIDTH,
1869
+ MATCH_CHIPS_FADE_MS,
1870
+ MatchChips,
1845
1871
  PREVIEW_ASPECT,
1846
1872
  PackProvider,
1847
1873
  PlayRoute,