@tastic/profile 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -180,7 +180,7 @@ Same `profiles`/`defaultColor`/`onCreate`/`onSave`/`onDelete` props as `Profiles
180
180
  `ProfileEditPatch`-shaped, still deliberately not widened to your own richer `CreateProfileInput`;
181
181
  compose any extra required fields (a key/control scheme, whatever's specific to your game) in your
182
182
  own closure at the call site, exactly as you already would calling `ProfilesManager` directly -
183
- plus `onBack`. `fg`/`fgMuted`/`bg`/`cardBg`/`titleVariant`/`colorPreview` are all independently
183
+ plus `onBack`. `fg`/`fgMuted`/`bg`/`titleVariant`/`colorPreview` are all independently
184
184
  optional and forward straight through to the internal `ProfilesManager` (`bg` is new here -
185
185
  `ProfilesManager` itself has no `bg`, since it doesn't own a container `View` of its own).
186
186
 
@@ -312,13 +312,10 @@ it.
312
312
  saved color live." Cleared only when that seat's own profile selection genuinely changes, never by
313
313
  merely leaving and returning to the screen.
314
314
 
315
- None of these three are exports of this package today. `lastGuestColor`/`lastCpuColor` predate it
316
- entirely, and `profileOverride` is still hand-rolled per app - typically alongside
317
- `createProfileSelectionSlice`'s own selection state, in the same seat-scoped slice (see e.g.
318
- BoxHockey's `src/redux/seatColorsSlice.ts`) or folded into the app's own `gameSlice.ts` when there's
319
- no dedicated slice to put it in (see Snake's `src/redux/gameSlice.ts`). This section exists so a new
320
- app's author reads the convention here first, rather than hand-rolling something that quietly can't
321
- interop with the rest of the fleet.
315
+ All three live in one slice from this package's `createSeatColorsSlice` (since 0.5.0), which every
316
+ flagship game uses (see e.g. BoxHockey's or Snake's `src/redux/seatColorsSlice.ts`). This section
317
+ exists so a new app's author reads the convention here first, rather than hand-rolling something
318
+ that quietly can't interop with the rest of the fleet.
322
319
 
323
320
  The resolution order at a loadout/lobby screen, for one seat:
324
321
 
package/dist/index.d.mts CHANGED
@@ -77,12 +77,11 @@ interface ProfilesManagerProps {
77
77
  ref?: Ref<ProfilesManagerHandle>;
78
78
  fg?: string;
79
79
  fgMuted?: string;
80
- cardBg?: string;
81
80
  titleVariant?: MD3TextVariant$1;
82
81
  colorPreview?: (hex: string) => string;
83
82
  useColorPickerAlign?: (open: boolean, contentWidth: number, contentHeight: number) => ProfilePickerAlignResult;
84
83
  }
85
- declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, cardBg: cardBgOverride, titleVariant, colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps): react.JSX.Element;
84
+ declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant, colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps): react.JSX.Element;
86
85
 
87
86
  type MD3TextVariant = 'displayLarge' | 'displayMedium' | 'displaySmall' | 'headlineLarge' | 'headlineMedium' | 'headlineSmall' | 'titleLarge' | 'titleMedium' | 'titleSmall' | 'labelLarge' | 'labelMedium' | 'labelSmall' | 'bodyLarge' | 'bodyMedium' | 'bodySmall';
88
87
  interface ProfilesScreenProps {
@@ -95,12 +94,11 @@ interface ProfilesScreenProps {
95
94
  fg?: string;
96
95
  fgMuted?: string;
97
96
  bg?: string;
98
- cardBg?: string;
99
97
  titleVariant?: MD3TextVariant;
100
98
  colorPreview?: (hex: string) => string;
101
99
  useColorPickerAlign?: ProfilesManagerProps['useColorPickerAlign'];
102
100
  }
103
- declare function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, cardBg, titleVariant, colorPreview, useColorPickerAlign }: ProfilesScreenProps): react.JSX.Element;
101
+ declare function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }: ProfilesScreenProps): react.JSX.Element;
104
102
 
105
103
  declare const MAX_PROFILE_NAME_LENGTH = 20;
106
104
  declare const MAX_TAG_LENGTH = 3;
package/dist/index.d.ts CHANGED
@@ -77,12 +77,11 @@ interface ProfilesManagerProps {
77
77
  ref?: Ref<ProfilesManagerHandle>;
78
78
  fg?: string;
79
79
  fgMuted?: string;
80
- cardBg?: string;
81
80
  titleVariant?: MD3TextVariant$1;
82
81
  colorPreview?: (hex: string) => string;
83
82
  useColorPickerAlign?: (open: boolean, contentWidth: number, contentHeight: number) => ProfilePickerAlignResult;
84
83
  }
85
- declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, cardBg: cardBgOverride, titleVariant, colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps): react.JSX.Element;
84
+ declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant, colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps): react.JSX.Element;
86
85
 
87
86
  type MD3TextVariant = 'displayLarge' | 'displayMedium' | 'displaySmall' | 'headlineLarge' | 'headlineMedium' | 'headlineSmall' | 'titleLarge' | 'titleMedium' | 'titleSmall' | 'labelLarge' | 'labelMedium' | 'labelSmall' | 'bodyLarge' | 'bodyMedium' | 'bodySmall';
88
87
  interface ProfilesScreenProps {
@@ -95,12 +94,11 @@ interface ProfilesScreenProps {
95
94
  fg?: string;
96
95
  fgMuted?: string;
97
96
  bg?: string;
98
- cardBg?: string;
99
97
  titleVariant?: MD3TextVariant;
100
98
  colorPreview?: (hex: string) => string;
101
99
  useColorPickerAlign?: ProfilesManagerProps['useColorPickerAlign'];
102
100
  }
103
- declare function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, cardBg, titleVariant, colorPreview, useColorPickerAlign }: ProfilesScreenProps): react.JSX.Element;
101
+ declare function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }: ProfilesScreenProps): react.JSX.Element;
104
102
 
105
103
  declare const MAX_PROFILE_NAME_LENGTH = 20;
106
104
  declare const MAX_TAG_LENGTH = 3;
package/dist/index.js CHANGED
@@ -327,19 +327,15 @@ function deriveTag(name) {
327
327
  const trimmed = name.trim();
328
328
  return trimmed ? trimmed[0].toUpperCase() : "";
329
329
  }
330
- function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, cardBg: cardBgOverride, titleVariant = "headlineSmall", colorPreview: colorPreviewProp, useColorPickerAlign }) {
330
+ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant = "headlineSmall", colorPreview: colorPreviewProp, useColorPickerAlign }) {
331
331
  const contextColorPreview = useProfileColorPreview();
332
332
  const colorPreview = colorPreviewProp ?? contextColorPreview;
333
- const { dark, colors } = (0, import_auto_paper3.useAutoPaperTheme)();
333
+ const { dark } = (0, import_auto_paper3.useAutoPaperTheme)();
334
334
  const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
335
335
  const { error: showErrorToast } = (0, import_toaster.useToast)();
336
336
  const host = (0, import_hud2.usePopoverHost)();
337
337
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
338
338
  const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
339
- const cardBg = cardBgOverride ?? (dark ? "#111111" : "#F2F2F2");
340
- const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
341
- const cancelBg = dark ? "#2A2A2A" : "#E0E0E0";
342
- const cancelFg = dark ? "#FFFFFF" : "#000000";
343
339
  const [editingId, setEditingId] = (0, import_react3.useState)(null);
344
340
  const [draftName, setDraftName] = (0, import_react3.useState)("");
345
341
  const [draftColor, setDraftColor] = (0, import_react3.useState)(defaultColor);
@@ -426,17 +422,7 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
426
422
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper3.Text, { variant: "bodyLarge", style: [styles3.rowName, { color: fg }], numberOfLines: 1, children: profile.name })
427
423
  ] }) }, profile.id);
428
424
  }) }) }),
429
- confirmDeleteProfile && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper3.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: styles3.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: [styles3.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }], children: [
430
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper3.Icon, { source: "alert-outline", size: 64, color: colors.danger }),
431
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper3.Text, { variant: "headlineLarge", style: [styles3.overlayTitle, { color: colors.danger }], children: "Delete Profile?" }),
432
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native_paper3.Text, { variant: "bodyLarge", style: [styles3.overlayBody, { color: fg }], children: [
433
- "This permanently deletes ",
434
- confirmDeleteProfile.name,
435
- "\u2019s profile. This cannot be undone."
436
- ] }),
437
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper3.Button, { mode: "contained", onPress: () => setConfirmDeleteId(null), style: styles3.overlayButton, buttonColor: cancelBg, textColor: cancelFg, children: "Cancel" }),
438
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper3.Button, { mode: "contained", onPress: handleConfirmDelete, style: styles3.overlayButton, buttonColor: colors.danger, textColor: colors.onDanger, children: "Delete" })
439
- ] }) }) })
425
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_hud2.ConfirmDialog, { visible: confirmDeleteProfile !== null, title: "Delete Profile?", message: confirmDeleteProfile ? `This permanently deletes ${confirmDeleteProfile.name}\u2019s profile. This cannot be undone.` : "", confirmLabel: "Delete", cancelLabel: "Cancel", icon: "alert-outline", destructive: true, onConfirm: handleConfirmDelete, onCancel: () => setConfirmDeleteId(null) })
440
426
  ] });
441
427
  }
442
428
  function AlignedColorPicker({ useAlign, ...props }) {
@@ -507,27 +493,6 @@ var styles3 = import_react_native3.StyleSheet.create({
507
493
  borderStyle: "dashed",
508
494
  borderWidth: 2
509
495
  },
510
- overlay: {
511
- alignItems: "center",
512
- backgroundColor: "rgba(0,0,0,0.72)",
513
- bottom: 0,
514
- justifyContent: "center",
515
- left: 0,
516
- position: "absolute",
517
- right: 0,
518
- top: 0
519
- },
520
- overlayBody: { textAlign: "center" },
521
- overlayButton: { width: 160 },
522
- overlayCard: {
523
- alignItems: "center",
524
- borderRadius: 20,
525
- borderWidth: 1,
526
- gap: 16,
527
- maxWidth: 360,
528
- padding: 32
529
- },
530
- overlayTitle: { fontWeight: "bold", marginBottom: -8 },
531
496
  row: {
532
497
  borderRadius: 8,
533
498
  paddingVertical: 8
@@ -562,7 +527,7 @@ var import_feedback_press3 = require("@rific/feedback-press");
562
527
  var import_react4 = require("react");
563
528
  var import_react_native4 = require("react-native");
564
529
  var import_jsx_runtime5 = require("react/jsx-runtime");
565
- function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, cardBg, titleVariant, colorPreview, useColorPickerAlign }) {
530
+ function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }) {
566
531
  const { dark } = (0, import_auto_paper4.useAutoPaperTheme)();
567
532
  const managerRef = (0, import_react4.useRef)(null);
568
533
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
@@ -571,7 +536,7 @@ function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, on
571
536
  managerRef.current?.commitPendingEdit();
572
537
  onBack();
573
538
  };
574
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.View, { style: [styles4.container, { backgroundColor: bg }], children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ProfilesManager, { ref: managerRef, profiles, defaultColor, onCreate, onSave, onDelete, fg, fgMuted, cardBg, titleVariant, colorPreview, useColorPickerAlign, headerLeft: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: handleBack, accessibilityLabel: "Back" }) }) });
539
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.View, { style: [styles4.container, { backgroundColor: bg }], children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ProfilesManager, { ref: managerRef, profiles, defaultColor, onCreate, onSave, onDelete, fg, fgMuted, titleVariant, colorPreview, useColorPickerAlign, headerLeft: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: handleBack, accessibilityLabel: "Back" }) }) });
575
540
  }
576
541
  var styles4 = import_react_native4.StyleSheet.create({
577
542
  container: {
package/dist/index.mjs CHANGED
@@ -239,10 +239,10 @@ import { defaultColors, useAutoPaperTheme } from "@rific/auto-paper";
239
239
  import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
240
240
  import { useFocusChain } from "@rific/focus-chain";
241
241
  import { useToast } from "@rific/toaster";
242
- import { getInlineColorPickerContentSize, InlineColorPicker, usePopoverHost } from "@tastic/hud";
242
+ import { ConfirmDialog, getInlineColorPickerContentSize, InlineColorPicker, usePopoverHost } from "@tastic/hud";
243
243
  import { useCallback, useEffect, useImperativeHandle, useMemo as useMemo2, useRef, useState } from "react";
244
244
  import { KeyboardAvoidingView, Platform, ScrollView as ScrollView2, StyleSheet as StyleSheet3, useWindowDimensions, View as View3 } from "react-native";
245
- import { Button, Icon as Icon3, Portal, Text as Text3, TextInput } from "react-native-paper";
245
+ import { Icon as Icon3, Text as Text3, TextInput } from "react-native-paper";
246
246
  import { useSafeAreaInsets } from "react-native-safe-area-context";
247
247
 
248
248
  // src/profilesValidation.ts
@@ -284,19 +284,15 @@ function deriveTag(name) {
284
284
  const trimmed = name.trim();
285
285
  return trimmed ? trimmed[0].toUpperCase() : "";
286
286
  }
287
- function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, cardBg: cardBgOverride, titleVariant = "headlineSmall", colorPreview: colorPreviewProp, useColorPickerAlign }) {
287
+ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant = "headlineSmall", colorPreview: colorPreviewProp, useColorPickerAlign }) {
288
288
  const contextColorPreview = useProfileColorPreview();
289
289
  const colorPreview = colorPreviewProp ?? contextColorPreview;
290
- const { dark, colors } = useAutoPaperTheme();
290
+ const { dark } = useAutoPaperTheme();
291
291
  const insets = useSafeAreaInsets();
292
292
  const { error: showErrorToast } = useToast();
293
293
  const host = usePopoverHost();
294
294
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
295
295
  const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
296
- const cardBg = cardBgOverride ?? (dark ? "#111111" : "#F2F2F2");
297
- const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
298
- const cancelBg = dark ? "#2A2A2A" : "#E0E0E0";
299
- const cancelFg = dark ? "#FFFFFF" : "#000000";
300
296
  const [editingId, setEditingId] = useState(null);
301
297
  const [draftName, setDraftName] = useState("");
302
298
  const [draftColor, setDraftColor] = useState(defaultColor);
@@ -383,17 +379,7 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
383
379
  /* @__PURE__ */ jsx4(Text3, { variant: "bodyLarge", style: [styles3.rowName, { color: fg }], numberOfLines: 1, children: profile.name })
384
380
  ] }) }, profile.id);
385
381
  }) }) }),
386
- confirmDeleteProfile && /* @__PURE__ */ jsx4(Portal, { children: /* @__PURE__ */ jsx4(View3, { style: styles3.overlay, children: /* @__PURE__ */ jsxs2(View3, { style: [styles3.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }], children: [
387
- /* @__PURE__ */ jsx4(Icon3, { source: "alert-outline", size: 64, color: colors.danger }),
388
- /* @__PURE__ */ jsx4(Text3, { variant: "headlineLarge", style: [styles3.overlayTitle, { color: colors.danger }], children: "Delete Profile?" }),
389
- /* @__PURE__ */ jsxs2(Text3, { variant: "bodyLarge", style: [styles3.overlayBody, { color: fg }], children: [
390
- "This permanently deletes ",
391
- confirmDeleteProfile.name,
392
- "\u2019s profile. This cannot be undone."
393
- ] }),
394
- /* @__PURE__ */ jsx4(Button, { mode: "contained", onPress: () => setConfirmDeleteId(null), style: styles3.overlayButton, buttonColor: cancelBg, textColor: cancelFg, children: "Cancel" }),
395
- /* @__PURE__ */ jsx4(Button, { mode: "contained", onPress: handleConfirmDelete, style: styles3.overlayButton, buttonColor: colors.danger, textColor: colors.onDanger, children: "Delete" })
396
- ] }) }) })
382
+ /* @__PURE__ */ jsx4(ConfirmDialog, { visible: confirmDeleteProfile !== null, title: "Delete Profile?", message: confirmDeleteProfile ? `This permanently deletes ${confirmDeleteProfile.name}\u2019s profile. This cannot be undone.` : "", confirmLabel: "Delete", cancelLabel: "Cancel", icon: "alert-outline", destructive: true, onConfirm: handleConfirmDelete, onCancel: () => setConfirmDeleteId(null) })
397
383
  ] });
398
384
  }
399
385
  function AlignedColorPicker({ useAlign, ...props }) {
@@ -464,27 +450,6 @@ var styles3 = StyleSheet3.create({
464
450
  borderStyle: "dashed",
465
451
  borderWidth: 2
466
452
  },
467
- overlay: {
468
- alignItems: "center",
469
- backgroundColor: "rgba(0,0,0,0.72)",
470
- bottom: 0,
471
- justifyContent: "center",
472
- left: 0,
473
- position: "absolute",
474
- right: 0,
475
- top: 0
476
- },
477
- overlayBody: { textAlign: "center" },
478
- overlayButton: { width: 160 },
479
- overlayCard: {
480
- alignItems: "center",
481
- borderRadius: 20,
482
- borderWidth: 1,
483
- gap: 16,
484
- maxWidth: 360,
485
- padding: 32
486
- },
487
- overlayTitle: { fontWeight: "bold", marginBottom: -8 },
488
453
  row: {
489
454
  borderRadius: 8,
490
455
  paddingVertical: 8
@@ -519,7 +484,7 @@ import { IconButton } from "@rific/feedback-press";
519
484
  import { useRef as useRef2 } from "react";
520
485
  import { StyleSheet as StyleSheet4, View as View4 } from "react-native";
521
486
  import { jsx as jsx5 } from "react/jsx-runtime";
522
- function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, cardBg, titleVariant, colorPreview, useColorPickerAlign }) {
487
+ function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }) {
523
488
  const { dark } = useAutoPaperTheme2();
524
489
  const managerRef = useRef2(null);
525
490
  const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
@@ -528,7 +493,7 @@ function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, on
528
493
  managerRef.current?.commitPendingEdit();
529
494
  onBack();
530
495
  };
531
- return /* @__PURE__ */ jsx5(View4, { style: [styles4.container, { backgroundColor: bg }], children: /* @__PURE__ */ jsx5(ProfilesManager, { ref: managerRef, profiles, defaultColor, onCreate, onSave, onDelete, fg, fgMuted, cardBg, titleVariant, colorPreview, useColorPickerAlign, headerLeft: /* @__PURE__ */ jsx5(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: handleBack, accessibilityLabel: "Back" }) }) });
496
+ return /* @__PURE__ */ jsx5(View4, { style: [styles4.container, { backgroundColor: bg }], children: /* @__PURE__ */ jsx5(ProfilesManager, { ref: managerRef, profiles, defaultColor, onCreate, onSave, onDelete, fg, fgMuted, titleVariant, colorPreview, useColorPickerAlign, headerLeft: /* @__PURE__ */ jsx5(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: handleBack, accessibilityLabel: "Back" }) }) });
532
497
  }
533
498
  var styles4 = StyleSheet4.create({
534
499
  container: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tastic/profile",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Player profile identity for local-multiplayer React Native games: a saved name/color/tag a player selects per seat, with a picker, an inline-editable roster screen, and a reusable identity badge, deliberately blind to any app-specific fields (control scheme, stats) an app layers on top",
5
5
  "keywords": [
6
6
  "react-native",
@@ -65,13 +65,13 @@
65
65
  "@infinitetoken/eslint-config": "0.2.0",
66
66
  "@infinitetoken/jest-config": "^0.4.0",
67
67
  "@infinitetoken/tsconfig": "^0.5.0",
68
- "@rific/auto-paper": "^0.12.0",
69
- "@rific/core": "^0.2.0",
70
- "@rific/feedback-press": "^0.12.0",
71
- "@rific/focus-chain": "^0.4.7",
72
- "@rific/toaster": "^0.7.0",
73
- "@tastic/core": "^0.9.0",
74
- "@tastic/hud": "^0.12.0",
68
+ "@rific/auto-paper": "^0.13.2",
69
+ "@rific/core": "^0.3.1",
70
+ "@rific/feedback-press": "^0.13.0",
71
+ "@rific/focus-chain": "^0.4.8",
72
+ "@rific/toaster": "^0.8.0",
73
+ "@tastic/core": "^0.14.0",
74
+ "@tastic/hud": "^0.19.0",
75
75
  "@testing-library/dom": "^10.4.1",
76
76
  "@testing-library/react": "^16.3.2",
77
77
  "@types/jest": "^30.0.0",
@@ -85,7 +85,7 @@
85
85
  "prettier": "^3.8.3",
86
86
  "react": "^19.0.0",
87
87
  "react-dom": "^19.0.0",
88
- "react-native": "^0.85.3",
88
+ "react-native": "^0.86.3",
89
89
  "react-native-paper": "^5.15.2",
90
90
  "react-native-safe-area-context": "^5.0.0",
91
91
  "tsup": "^8.0.0",
@@ -2,10 +2,10 @@ import { defaultColors, useAutoPaperTheme } from '@rific/auto-paper'
2
2
  import { TouchableRipple } from '@rific/feedback-press'
3
3
  import { useFocusChain } from '@rific/focus-chain'
4
4
  import { useToast } from '@rific/toaster'
5
- import { getInlineColorPickerContentSize, InlineColorPicker, PopoverHost, usePopoverHost } from '@tastic/hud'
5
+ import { ConfirmDialog, getInlineColorPickerContentSize, InlineColorPicker, PopoverHost, usePopoverHost } from '@tastic/hud'
6
6
  import { ComponentProps, ReactNode, Ref, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'
7
7
  import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, useWindowDimensions, View } from 'react-native'
8
- import { Button, Icon, Portal, Text, TextInput } from 'react-native-paper'
8
+ import { Icon, Text, TextInput } from 'react-native-paper'
9
9
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
10
10
 
11
11
  import { ProfileChip } from './ProfileChip'
@@ -62,17 +62,13 @@ export interface ProfilesManagerProps {
62
62
  // React 19 accepts `ref` as a plain prop on a function component (no forwardRef needed) - see
63
63
  // ProfilesManagerHandle's own doc for why a host may need this.
64
64
  ref?: Ref<ProfilesManagerHandle>
65
- // Independently optional overrides for this screen's text/card colors - each defaults to the same
66
- // literal-black/white-by-appearance formula as before when omitted, so every existing caller
67
- // (none of which pass these) renders identically to before. Same convention as @tastic/hud's
68
- // BaseStatsScreen fg/cardBg overrides, for a caller with its own app-wide chrome palette instead
69
- // of that literal black/white convention. cardBorder/cancelBg/cancelFg have no override, matching
70
- // BaseStatsScreen's identical reasoning for cardBorder: their low-alpha/neutral values already
71
- // read fine against any cardBg, and (for Cancel specifically) are deliberately neutral rather than
72
- // theme-derived so a host's own live gameplay/seat color can't land there by coincidence.
65
+ // Independently optional overrides for this screen's text colors - each defaults to the same
66
+ // literal-black/white-by-appearance formula as before when omitted. Same convention as @tastic/hud's
67
+ // BaseStatsScreen fg override, for a caller with its own app-wide chrome palette instead of that
68
+ // literal black/white convention. (cardBg, which only colored the delete confirmation, went with it
69
+ // when that became hud's ConfirmDialog, 0.13.0.)
73
70
  fg?: string
74
71
  fgMuted?: string
75
- cardBg?: string
76
72
  // 'headlineSmall' (this screen's own default, below), matching BaseStatsScreen's own settled
77
73
  // default and the reasoning behind it - this title sits next to a small headerLeft back-button
78
74
  // glyph the same way, and the previous hardcoded 'displaySmall' read as oversized there too.
@@ -115,12 +111,12 @@ function deriveTag(name: string): string {
115
111
  // really unmounts it on a given host's router - see ProfilesManagerHandle's own doc for the
116
112
  // react-navigation-web case where it doesn't, and why a host there needs the `ref` escape hatch
117
113
  // instead of relying on the unmount fallback alone.
118
- export function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, cardBg: cardBgOverride, titleVariant = 'headlineSmall', colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps) {
114
+ export function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant = 'headlineSmall', colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps) {
119
115
  // An explicit prop wins; otherwise the app-wide ProfileColorProvider's, so the swatch previews
120
116
  // match the ProfileChips drawn in the list.
121
117
  const contextColorPreview = useProfileColorPreview()
122
118
  const colorPreview = colorPreviewProp ?? contextColorPreview
123
- const { dark, colors } = useAutoPaperTheme()
119
+ const { dark } = useAutoPaperTheme()
124
120
  const insets = useSafeAreaInsets()
125
121
  const { error: showErrorToast } = useToast()
126
122
  // Only one row is ever in edit mode at a time (see commitEdit, called before any new row opens),
@@ -128,12 +124,6 @@ export function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDe
128
124
  const host = usePopoverHost()
129
125
  const fg = fgOverride ?? (dark ? '#FFFFFF' : '#000000')
130
126
  const fgMuted = fgMutedOverride ?? (dark ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)')
131
- const cardBg = cardBgOverride ?? (dark ? '#111111' : '#F2F2F2')
132
- const cardBorder = dark ? 'rgba(255,255,255,0.2)' : 'rgba(0,0,0,0.2)'
133
- // A contained neutral, not an outlined one - reads as "the other button" next to Delete's solid
134
- // danger fill, rather than looking unfinished/secondary beside it.
135
- const cancelBg = dark ? '#2A2A2A' : '#E0E0E0'
136
- const cancelFg = dark ? '#FFFFFF' : '#000000'
137
127
 
138
128
  // 'new' clears back to null once a commit resolves - there's only ever one row open at a time.
139
129
  const [editingId, setEditingId] = useState<string | 'new' | null>(null)
@@ -296,32 +286,11 @@ export function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDe
296
286
  </ScrollView>
297
287
  </KeyboardAvoidingView>
298
288
 
299
- {confirmDeleteProfile && (
300
- <Portal>
301
- <View style={styles.overlay}>
302
- <View style={[styles.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }]}>
303
- <Icon source='alert-outline' size={64} color={colors.danger} />
304
- <Text variant='headlineLarge' style={[styles.overlayTitle, { color: colors.danger }]}>
305
- Delete Profile?
306
- </Text>
307
- <Text variant='bodyLarge' style={[styles.overlayBody, { color: fg }]}>
308
- This permanently deletes {confirmDeleteProfile.name}&rsquo;s profile. This cannot be undone.
309
- </Text>
310
- {/* Contained neutral, not buttonColor={colors.primary} - colors.primary is often a
311
- live gameplay/seat color in host apps, so a contained Cancel in that color would just
312
- be whatever's currently painted there, with no relation to "safe to tap". */}
313
- <Button mode='contained' onPress={() => setConfirmDeleteId(null)} style={styles.overlayButton} buttonColor={cancelBg} textColor={cancelFg}>
314
- Cancel
315
- </Button>
316
- {/* colors.danger/onDanger - auto-paper's actual semantic role for this, not a seat's
317
- own live color by luck of the default palette. */}
318
- <Button mode='contained' onPress={handleConfirmDelete} style={styles.overlayButton} buttonColor={colors.danger} textColor={colors.onDanger}>
319
- Delete
320
- </Button>
321
- </View>
322
- </View>
323
- </Portal>
324
- )}
289
+ {/* hud's ConfirmDialog, the one confirm card every app in the fleet uses, so this reads like the
290
+ Reset Everything?/New Game confirms beside it (Jay, 2026-10-15: one look for every dialog). It
291
+ used to be its own card here, in this screen's literal black/white (or the host's cardBg), with
292
+ two stacked contained buttons. */}
293
+ <ConfirmDialog visible={confirmDeleteProfile !== null} title='Delete Profile?' message={confirmDeleteProfile ? `This permanently deletes ${confirmDeleteProfile.name}\u2019s profile. This cannot be undone.` : ''} confirmLabel='Delete' cancelLabel='Cancel' icon='alert-outline' destructive onConfirm={handleConfirmDelete} onCancel={() => setConfirmDeleteId(null)} />
325
294
  </>
326
295
  )
327
296
  }
@@ -466,27 +435,6 @@ const styles = StyleSheet.create({
466
435
  borderStyle: 'dashed',
467
436
  borderWidth: 2
468
437
  },
469
- overlay: {
470
- alignItems: 'center',
471
- backgroundColor: 'rgba(0,0,0,0.72)',
472
- bottom: 0,
473
- justifyContent: 'center',
474
- left: 0,
475
- position: 'absolute',
476
- right: 0,
477
- top: 0
478
- },
479
- overlayBody: { textAlign: 'center' },
480
- overlayButton: { width: 160 },
481
- overlayCard: {
482
- alignItems: 'center',
483
- borderRadius: 20,
484
- borderWidth: 1,
485
- gap: 16,
486
- maxWidth: 360,
487
- padding: 32
488
- },
489
- overlayTitle: { fontWeight: 'bold', marginBottom: -8 },
490
438
  row: {
491
439
  borderRadius: 8,
492
440
  paddingVertical: 8
@@ -33,11 +33,10 @@ export interface ProfilesScreenProps {
33
33
  // Independently optional overrides, each defaulting to the same literal-black/white-by-appearance
34
34
  // formula every current caller renders with unchanged (none of them pass these today) - same
35
35
  // convention as BaseStatsScreen's and ProfilesManager's own fg/bg-shaped overrides. fg/fgMuted/
36
- // cardBg/titleVariant/colorPreview are forwarded straight through to the internal ProfilesManager.
36
+ // titleVariant/colorPreview are forwarded straight through to the internal ProfilesManager.
37
37
  fg?: string
38
38
  fgMuted?: string
39
39
  bg?: string
40
- cardBg?: string
41
40
  titleVariant?: MD3TextVariant
42
41
  colorPreview?: (hex: string) => string
43
42
  // Forwarded straight through to the internal ProfilesManager - a placement hook for the color swatch popover, for
@@ -53,7 +52,7 @@ export interface ProfilesScreenProps {
53
52
  // still doesn't call useProfiles()/useGameStats() (or any host-specific hook) itself - those two
54
53
  // hooks' shapes are genuinely app-specific (different CreateProfileInput/GameStatsContextValue per
55
54
  // app), so this stays props-in/callbacks-out, exactly like ProfilesManager itself.
56
- export function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, cardBg, titleVariant, colorPreview, useColorPickerAlign }: ProfilesScreenProps) {
55
+ export function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }: ProfilesScreenProps) {
57
56
  const { dark } = useAutoPaperTheme()
58
57
  // Owns the ref itself - nothing outside this component ever needs it, since this is now the one
59
58
  // thing that both renders ProfilesManager AND owns the only navigation trigger next to it.
@@ -75,7 +74,7 @@ export function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDel
75
74
 
76
75
  return (
77
76
  <View style={[styles.container, { backgroundColor: bg }]}>
78
- <ProfilesManager ref={managerRef} profiles={profiles} defaultColor={defaultColor} onCreate={onCreate} onSave={onSave} onDelete={onDelete} fg={fg} fgMuted={fgMuted} cardBg={cardBg} titleVariant={titleVariant} colorPreview={colorPreview} useColorPickerAlign={useColorPickerAlign} headerLeft={<IconButton icon='arrow-left' iconColor={fg} size={24} onPress={handleBack} accessibilityLabel='Back' />} />
77
+ <ProfilesManager ref={managerRef} profiles={profiles} defaultColor={defaultColor} onCreate={onCreate} onSave={onSave} onDelete={onDelete} fg={fg} fgMuted={fgMuted} titleVariant={titleVariant} colorPreview={colorPreview} useColorPickerAlign={useColorPickerAlign} headerLeft={<IconButton icon='arrow-left' iconColor={fg} size={24} onPress={handleBack} accessibilityLabel='Back' />} />
79
78
  </View>
80
79
  )
81
80
  }