@tatchi-xyz/sdk 0.32.1 → 0.32.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-D2eRb2-S.css → PasskeyAuthMenu-mMygL3xX.css} +137 -47
  2. package/dist/cjs/react/components/PasskeyAuthMenu/PasskeyAuthMenu-mMygL3xX.css.map +1 -0
  3. package/dist/cjs/react/components/PasskeyAuthMenu/client.js +2 -3
  4. package/dist/cjs/react/components/PasskeyAuthMenu/client.js.map +1 -1
  5. package/dist/cjs/react/components/PasskeyAuthMenu/controller/mode.js +7 -2
  6. package/dist/cjs/react/components/PasskeyAuthMenu/controller/mode.js.map +1 -1
  7. package/dist/cjs/react/components/PasskeyAuthMenu/controller/usePasskeyAuthMenuController.js +4 -1
  8. package/dist/cjs/react/components/PasskeyAuthMenu/controller/usePasskeyAuthMenuController.js.map +1 -1
  9. package/dist/cjs/react/components/PasskeyAuthMenu/hydrationContext.js +20 -0
  10. package/dist/cjs/react/components/PasskeyAuthMenu/hydrationContext.js.map +1 -0
  11. package/dist/cjs/react/components/PasskeyAuthMenu/passkeyAuthMenuCompat.js +1 -1
  12. package/dist/cjs/react/components/PasskeyAuthMenu/shell.js +64 -30
  13. package/dist/cjs/react/components/PasskeyAuthMenu/shell.js.map +1 -1
  14. package/dist/cjs/react/components/PasskeyAuthMenu/skeleton.js +155 -101
  15. package/dist/cjs/react/components/PasskeyAuthMenu/skeleton.js.map +1 -1
  16. package/dist/cjs/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js +5 -1
  17. package/dist/cjs/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js.map +1 -1
  18. package/dist/cjs/react/index.js +2 -2
  19. package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-qTHAv58Z.css → PasskeyAuthMenu-BihXvuII.css} +137 -47
  20. package/dist/esm/react/components/PasskeyAuthMenu/PasskeyAuthMenu-BihXvuII.css.map +1 -0
  21. package/dist/esm/react/components/PasskeyAuthMenu/client.js +2 -3
  22. package/dist/esm/react/components/PasskeyAuthMenu/client.js.map +1 -1
  23. package/dist/esm/react/components/PasskeyAuthMenu/controller/mode.js +7 -3
  24. package/dist/esm/react/components/PasskeyAuthMenu/controller/mode.js.map +1 -1
  25. package/dist/esm/react/components/PasskeyAuthMenu/controller/usePasskeyAuthMenuController.js +4 -1
  26. package/dist/esm/react/components/PasskeyAuthMenu/controller/usePasskeyAuthMenuController.js.map +1 -1
  27. package/dist/esm/react/components/PasskeyAuthMenu/hydrationContext.js +17 -0
  28. package/dist/esm/react/components/PasskeyAuthMenu/hydrationContext.js.map +1 -0
  29. package/dist/esm/react/components/PasskeyAuthMenu/passkeyAuthMenuCompat.js +1 -1
  30. package/dist/esm/react/components/PasskeyAuthMenu/shell.js +64 -30
  31. package/dist/esm/react/components/PasskeyAuthMenu/shell.js.map +1 -1
  32. package/dist/esm/react/components/PasskeyAuthMenu/skeleton.js +155 -101
  33. package/dist/esm/react/components/PasskeyAuthMenu/skeleton.js.map +1 -1
  34. package/dist/esm/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js +5 -1
  35. package/dist/esm/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js.map +1 -1
  36. package/dist/esm/react/index.js +2 -2
  37. package/dist/esm/react/styles/styles.css +136 -46
  38. package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
  39. package/dist/types/src/react/components/PasskeyAuthMenu/client.d.ts +0 -1
  40. package/dist/types/src/react/components/PasskeyAuthMenu/client.d.ts.map +1 -1
  41. package/dist/types/src/react/components/PasskeyAuthMenu/controller/mode.d.ts +6 -1
  42. package/dist/types/src/react/components/PasskeyAuthMenu/controller/mode.d.ts.map +1 -1
  43. package/dist/types/src/react/components/PasskeyAuthMenu/controller/usePasskeyAuthMenuController.d.ts.map +1 -1
  44. package/dist/types/src/react/components/PasskeyAuthMenu/hydrationContext.d.ts +10 -0
  45. package/dist/types/src/react/components/PasskeyAuthMenu/hydrationContext.d.ts.map +1 -0
  46. package/dist/types/src/react/components/PasskeyAuthMenu/shell.d.ts +2 -1
  47. package/dist/types/src/react/components/PasskeyAuthMenu/shell.d.ts.map +1 -1
  48. package/dist/types/src/react/components/PasskeyAuthMenu/skeleton.d.ts +5 -1
  49. package/dist/types/src/react/components/PasskeyAuthMenu/skeleton.d.ts.map +1 -1
  50. package/dist/types/src/react/components/PasskeyAuthMenu/ui/ContentSwitcher.d.ts.map +1 -1
  51. package/dist/workers/offline-export-sw.js +156 -1
  52. package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
  53. package/dist/workers/web3authn-signer.worker.js +1360 -2
  54. package/dist/workers/web3authn-vrf.worker.js +2857 -2
  55. package/package.json +4 -2
  56. package/dist/cjs/react/components/PasskeyAuthMenu/PasskeyAuthMenu-D2eRb2-S.css.map +0 -1
  57. package/dist/esm/react/components/PasskeyAuthMenu/PasskeyAuthMenu-qTHAv58Z.css.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PasskeyAuthMenu-BihXvuII.css","names":[],"sources":["../../../../../src/react/components/PasskeyAuthMenu/PasskeyAuthMenu.css"],"sourcesContent":["/* Root container */\n.w3a-no-animation {\n animation: none !important;\n transition: none !important;\n}\n\n.w3a-signup-menu-root {\n /* CSS sentinel (used by tests/diagnostics to confirm stylesheet is applied) */\n --w3a-pam2-css-ready: 1;\n position: relative;\n /* relative position to anchor back button */\n width: min(100dvw, 420px);\n max-width: 100dvw;\n min-width: 330px;\n min-height: 250px;\n color: var(--w3a-colors-textPrimary);\n background: var(--w3a-colors-colorBackground);\n border: 1px solid var(--w3a-colors-borderPrimary);\n border-radius: 3rem;\n box-shadow: var(--w3a-shadows-lg);\n padding: var(--w3a-spacing-lg);\n padding-top: calc(var(--w3a-spacing-lg) + 4px);\n position: relative;\n display: flex;\n flex-direction: column;\n transition: height 260ms cubic-bezier(0.2, 0.8, 0.2, 1), min-height 260ms cubic-bezier(0.2, 0.8, 0.2, 1);\n will-change: height, min-height;\n}\n\n/* Back button */\n.w3a-back-button {\n position: absolute;\n top: 1rem;\n left: 1rem;\n width: 48px;\n height: 48px;\n padding: 0;\n aspect-ratio: 1 / 1;\n display: grid;\n place-items: center;\n line-height: 0;\n border-radius: 50%;\n color: var(--w3a-colors-textPrimary);\n background: transparent;\n cursor: pointer;\n border: none;\n z-index: 3;\n transition: transform 120ms ease, background-color 160ms ease, opacity 220ms ease;\n opacity: 0;\n pointer-events: none;\n filter: blur(0.2px);\n}\n\n.w3a-back-button.is-visible {\n opacity: 1;\n pointer-events: auto;\n filter: none;\n}\n\n.w3a-back-button:hover {\n transform: scale(1.02);\n background: var(--w3a-colors-surface);\n}\n\n.w3a-back-button:active {\n transform: scale(0.96);\n}\n\n/* Header */\n.w3a-header {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n opacity: 1;\n height: auto;\n overflow: hidden;\n transition: opacity 240ms ease-out, height 260ms cubic-bezier(0.2, 0.8, 0.2, 1), padding 260ms cubic-bezier(0.2, 0.8, 0.2, 1), margin 260ms cubic-bezier(0.2, 0.8, 0.2, 1);\n pointer-events: auto;\n position: relative;\n will-change: height, opacity;\n}\n\n/* Hide header when waiting or scan device is active */\n.w3a-signup-menu-root[data-waiting=\"true\"] .w3a-header,\n.w3a-signup-menu-root[data-scan-device=\"true\"] .w3a-header {\n opacity: 0;\n height: 0px;\n padding-top: 0px;\n padding-bottom: 0px;\n margin-top: 0px;\n margin-bottom: 0px;\n pointer-events: none;\n}\n\n/* Hide header when email recovery is active */\n.w3a-signup-menu-root[data-email-recovery=\"true\"] .w3a-header {\n opacity: 0;\n height: 0px;\n padding-top: 0px;\n padding-bottom: 0px;\n margin-top: 0px;\n margin-bottom: 0px;\n pointer-events: none;\n}\n\n/* Adjust content area when header is hidden */\n.w3a-signup-menu-root[data-waiting=\"true\"] .w3a-content-area,\n.w3a-signup-menu-root[data-scan-device=\"true\"] .w3a-content-area {\n flex: 1 1 auto;\n transition: height 260ms cubic-bezier(0.2, 0.8, 0.2, 1);\n}\n\n/* Adjust content area when email recovery is active */\n.w3a-signup-menu-root[data-email-recovery=\"true\"] .w3a-content-area {\n flex: 1 1 auto;\n transition: height 260ms cubic-bezier(0.2, 0.8, 0.2, 1);\n}\n\n/* Specific height adjustments for different states */\n.w3a-signup-menu-root[data-waiting=\"true\"] {\n min-height: 200px;\n}\n\n.w3a-signup-menu-root[data-scan-device=\"true\"] {\n min-height: 300px;\n}\n\n.w3a-signup-menu-root[data-email-recovery=\"true\"] {\n min-height: 320px;\n}\n\n.w3a-title {\n font-size: 24px;\n font-weight: 700;\n margin: 0.5rem 0.75rem;\n}\n\n.w3a-subhead {\n font-size: 1rem;\n font-weight: 500;\n line-height: 1;\n color: color-mix(in srgb, var(--w3a-colors-textSecondary), var(--w3a-colors-textPrimary) 20%);\n margin: 0;\n margin-bottom: 1rem;\n margin-left: 0.75rem;\n}\n\n/* Content switcher container */\n.w3a-content-switcher {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n /* ensure smooth height animations without spillover */\n transition: height 260ms cubic-bezier(0.2, 0.8, 0.2, 1);\n will-change: height;\n}\n\n/* Content area container */\n.w3a-content-area {\n position: relative;\n /* do not force-stretch; let content define intrinsic height */\n flex: 0 1 auto;\n display: flex;\n flex-direction: column;\n transition: height 260ms cubic-bezier(0.2, 0.8, 0.2, 1);\n}\n\n/* Intrinsic sizer wrapper inside content area used for height measurement */\n.w3a-content-sizer {\n display: block;\n width: 100%;\n}\n\n/* Default content */\n.w3a-signin-menu {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 0px;\n min-height: 250px;\n width: 100%;\n}\n\n@keyframes content-enter {\n from {\n opacity: 0;\n transform: scale(0.98) translateY(6px);\n }\n\n to {\n opacity: 1;\n transform: scale(1) translateY(0);\n }\n}\n\n@keyframes fade-in {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n}\n\n/* Social providers row */\n.w3a-social-row {\n display: flex;\n gap: 8px;\n margin-bottom: var(--w3a-spacing-sm);\n}\n\n.w3a-social-btn {\n height: 48px;\n flex: 1 1 0;\n min-width: 0;\n display: grid;\n place-items: center;\n cursor: pointer;\n color: var(--w3a-colors-textSecondary, #64748b);\n overflow: hidden;\n border: 1px solid var(--w3a-colors-borderPrimary);\n background: var(--w3a-colors-surface);\n border-radius: var(--w3a-border-radius-xl);\n /* no shadow requested */\n box-shadow: none;\n}\n\n.w3a-social-btn svg {\n color: var(--w3a-colors-textSecondary, #64748b);\n}\n\n.w3a-social-btn:hover {\n background: var(--w3a-colors-surface2);\n box-shadow: var(--w3a-shadows-sm);\n}\n\n.w3a-social-btn:hover svg {\n color: var(--w3a-colors-textPrimary, #1e293b);\n}\n\n/* Passkey row */\n.w3a-passkey-row {\n position: relative;\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.w3a-input-pill {\n position: relative;\n display: flex;\n flex: 1;\n align-items: center;\n padding: 0rem 1rem;\n height: 54px;\n gap: 8px;\n border: none;\n background: var(--w3a-colors-surface);\n border-radius: 2rem 2rem 2rem 2rem;\n box-shadow: none;\n overflow-x: hidden;\n transition: border-radius 150ms ease-in-out;\n}\n\n.w3a-input-pill.is-enabled {\n border-radius: 2rem 0.25rem 0.25rem 2rem;\n transition: border-radius 150ms ease-in-out;\n}\n\n.w3a-input-wrap {\n position: relative;\n flex: 1;\n height: 32px;\n display: flex;\n align-items: center;\n min-width: 0;\n /* allow input to shrink inside flex container without clipping */\n}\n\n.w3a-input {\n width: 100%;\n height: 32px;\n border: none;\n outline: none;\n background: transparent;\n color: var(--w3a-colors-textPrimary);\n font-size: 16px;\n padding: 0;\n min-width: 0;\n /* prevent overflow clipping in flex layouts */\n}\n\n/* Absolute status message anchored to bottom-right of the input area */\n.w3a-input::placeholder {\n /* Improve contrast for placeholders on dark */\n color: color-mix(in srgb, var(--w3a-colors-textSecondary), var(--w3a-colors-textPrimary) 35%);\n opacity: 0.95;\n}\n\n.w3a-postfix {\n position: absolute;\n top: 50%;\n left: 0;\n transform: translateY(-50%);\n color: var(--w3a-colors-textSecondary);\n font-size: 16px;\n white-space: nowrap;\n pointer-events: none;\n visibility: hidden;\n /* React enables once measured */\n will-change: left;\n transition: left 32ms ease;\n}\n\n.w3a-postfix.is-existing {\n color: var(--w3a-colors-success);\n}\n\n/* On focus, keep postfix subtle for readability */\n.w3a-input:focus~.w3a-postfix {\n color: var(--w3a-colors-textMuted);\n}\n\n.w3a-arrow-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 0;\n /* hidden footprint by default */\n padding: 0;\n background: transparent;\n border: 0;\n border-radius: 2rem 0.25rem 0.25rem 2rem;\n color: #fff;\n line-height: 0;\n cursor: pointer;\n opacity: 0;\n visibility: hidden;\n pointer-events: none;\n margin-left: -1rem;\n z-index: 1;\n border: 6px solid var(--w3a-colors-colorBackground, #fff);\n height: 64px;\n transition: transform 150ms ease,\n background-color 150ms ease,\n border-radius 150ms ease,\n opacity 150ms ease,\n width 150ms ease-in-out;\n}\n\n.w3a-arrow-btn.is-enabled {\n width: 100px;\n /* expand when enabled */\n /* Prefer themed primary; fall back to legacy then a sane default */\n background: var(--w3a-colors-primary, #2563eb);\n border-radius: 2rem;\n /* border-radius: 2rem; */\n opacity: 1;\n visibility: visible;\n pointer-events: auto;\n transition: transform 150ms ease,\n background-color 150ms ease,\n border-radius 150ms ease,\n opacity 150ms ease,\n width 150ms ease-in-out;\n}\n\n.w3a-arrow-btn.no-transition,\n.w3a-arrow-btn.no-transition.is-enabled {\n transition: none;\n}\n\n.w3a-arrow-btn .w3a-arrow-icon {\n color: #fff;\n}\n\n.w3a-arrow-btn.is-enabled:hover {\n /* transform: scale(1.02); */\n background: var(--w3a-colors-primaryHover, #1d4ed8);\n}\n\n.w3a-arrow-btn.is-enabled:active {\n transform: scale(0.96);\n}\n\n.w3a-arrow-btn:disabled {\n width: 0;\n cursor: not-allowed;\n opacity: 0.5;\n background: var(--w3a-colors-borderSecondary);\n transition: transform 150ms ease,\n background-color 150ms ease,\n border-radius 150ms ease,\n opacity 150ms ease,\n width 150ms ease-in-out;\n}\n\n.w3a-arrow-btn .w3a-arrow-label {\n margin-left: 8px;\n font-weight: 600;\n line-height: 1;\n}\n\n/* Animated arrow inside the continue button */\n.w3a-arrow-btn .stripe-arrow {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.w3a-arrow-btn .stripe-arrow>.HoverArrow {\n position: relative;\n margin-top: 0.1rem;\n margin-left: 8px;\n stroke-width: 2;\n fill: none;\n stroke: currentColor;\n}\n\n/* Horizontal line: fades in on hover of the button */\n.w3a-arrow-btn .stripe-arrow>.HoverArrow .HoverArrow__linePath {\n opacity: 0;\n transition: opacity 120ms cubic-bezier(0.215, 0.61, 0.355, 1);\n}\n\n/* Chevron: nudges right and slightly scales on hover */\n.w3a-arrow-btn .stripe-arrow>.HoverArrow .HoverArrow__tipPath {\n transition: transform 120ms cubic-bezier(0.215, 0.61, 0.355, 1);\n}\n\n.w3a-arrow-btn.is-enabled:hover .stripe-arrow>.HoverArrow .HoverArrow__linePath,\n.w3a-arrow-btn.is-enabled:focus-visible .stripe-arrow>.HoverArrow .HoverArrow__linePath {\n opacity: 1;\n}\n\n.w3a-arrow-btn.is-enabled:hover .stripe-arrow>.HoverArrow .HoverArrow__tipPath,\n.w3a-arrow-btn.is-enabled:focus-visible .stripe-arrow>.HoverArrow .HoverArrow__tipPath {\n transform: translateX(3px);\n}\n\n/* Segmented control */\n.w3a-seg {\n position: relative;\n min-height: 54px;\n overflow: hidden;\n padding: 5px;\n border: none;\n background: var(--w3a-colors-surface2);\n border-radius: var(--w3a-border-radius-xl);\n}\n\n.w3a-seg-active {\n position: absolute;\n top: 5px;\n bottom: 5px;\n left: 0;\n border-radius: var(--w3a-border-radius-xl);\n transition:\n transform 320ms cubic-bezier(0.2, 0.8, 0.2, 1),\n width 220ms ease,\n opacity 150ms ease;\n will-change: transform, width;\n pointer-events: none;\n}\n\n.w3a-seg-grid {\n display: flex;\n gap: 4px;\n height: 100%;\n position: relative;\n z-index: 1;\n}\n\n.w3a-seg-btn {\n flex: 1 1 0;\n min-width: 0;\n min-height: 44px;\n border-radius: 12px;\n background: transparent;\n /* Make inactive tabs more legible and clearly tappable */\n color: color-mix(in srgb, var(--w3a-colors-textSecondary), var(--w3a-colors-textPrimary) 45%);\n font-weight: 600;\n cursor: pointer;\n border: none;\n /* Immediate taps on mobile; opt-out of double-tap zoom heuristics */\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n transition: color 200ms ease, transform 120ms ease;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n white-space: normal;\n padding: 0 14px;\n font-size: clamp(13px, 3.6vw, 15px);\n}\n\n.w3a-seg-btn:hover {\n transform: scale(1.02);\n}\n\n.w3a-seg-btn:active {\n transform: scale(0.98);\n}\n\n.w3a-seg-btn.is-active {\n color: var(--w3a-colors-textPrimary);\n}\n\n.w3a-seg-btn:focus-visible {\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--w3a-colors-focus), transparent 60%);\n}\n\n@media (max-width: 420px) {\n .w3a-signup-menu-root {\n padding: 1rem;\n padding-top: calc(1rem + 4px);\n border-radius: 2rem;\n max-width: calc(100dvw - 0.25rem);\n }\n\n @supports (width: 1dvw) {\n .w3a-signup-menu-root {\n max-width: calc(100dvw - 0.25rem);\n }\n }\n\n .w3a-title {\n font-size: 20px;\n }\n\n .w3a-subhead {\n font-size: 0.9rem;\n margin-bottom: 0.75rem;\n }\n\n .w3a-input-pill {\n height: 48px;\n }\n\n .w3a-arrow-btn {\n height: 60px;\n }\n\n .w3a-arrow-btn.is-enabled {\n width: 60px;\n }\n\n .w3a-seg {\n min-height: 48px;\n padding: 4px;\n }\n\n .w3a-seg-grid {\n gap: 3px;\n }\n\n .w3a-seg-active {\n top: 4px;\n bottom: 4px;\n }\n\n .w3a-seg-btn {\n min-height: 42px;\n padding: 0 12px;\n font-size: clamp(12px, 3.4vw, 14px);\n }\n\n .w3a-back-button {\n top: -0.5rem;\n left: -0.5rem;\n }\n}\n\n/* Waiting state */\n.w3a-waiting {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n background: transparent;\n text-align: center;\n min-height: 200px;\n /* Quick fade with configurable delay for smoother handoff */\n animation: content-enter 200ms ease-out;\n animation-delay: var(--w3a-waiting-delay, 0ms);\n animation-fill-mode: both;\n}\n\n.w3a-waiting-message {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n}\n\n.w3a-waiting-text {\n font-size: 18px;\n font-weight: 600;\n}\n\n.w3a-waiting-subtext {\n font-size: 12px;\n font-weight: 500;\n line-height: 1.35;\n color: color-mix(in srgb, var(--w3a-colors-textSecondary), var(--w3a-colors-textPrimary) 25%);\n}\n\n.w3a-waiting-sdk-events {\n font-size: 11px;\n font-weight: 500;\n line-height: 1.35;\n white-space: pre-wrap;\n color: color-mix(in srgb, var(--w3a-colors-textSecondary), var(--w3a-colors-textPrimary) 15%);\n}\n\n.w3a-spinner {\n width: 36px;\n height: 36px;\n border-radius: 999px;\n border: 3px solid rgba(255, 255, 255, 0.15);\n border-top-color: var(--w3a-colors-primary);\n animation: w3a-spin 0.9s linear infinite;\n}\n\n/* Scan device content wrapper */\n.w3a-scan-device-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n background: transparent;\n text-align: center;\n min-height: 300px;\n width: 100%;\n overflow: hidden;\n animation: content-enter 240ms ease-out;\n animation-fill-mode: both;\n}\n\n/* Ensure QR code content stays within bounds */\n.w3a-scan-device-content .qr-code-container,\n.w3a-scan-device-content .qr-modal-backdrop,\n.w3a-scan-device-content .qr-modal-content {\n max-width: 100%;\n max-height: 100%;\n overflow: hidden;\n}\n\n.w3a-scan-device-content .qr-code-display {\n max-width: 280px;\n width: 100%;\n}\n\n/* Status message row */\n.w3a-status-row {\n position: absolute;\n font-size: 0.75rem;\n bottom: -10px;\n right: 0;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n}\n\n.w3a-status-message {\n font-size: 11px;\n font-weight: 500;\n}\n\n/* Section divider */\n.w3a-section-divider {\n display: flex;\n align-items: center;\n margin: var(--w3a-spacing-md) 0;\n position: relative;\n}\n\n.w3a-section-divider::before,\n.w3a-section-divider::after {\n content: '';\n flex: 1;\n height: 1px;\n background: var(--w3a-colors-borderSecondary);\n}\n\n.w3a-section-divider-text {\n padding: 0 var(--w3a-spacing-sm);\n font-size: 12px;\n color: var(--w3a-colors-textSecondary);\n font-weight: 500;\n background: var(--w3a-colors-colorBackground);\n}\n\n/* Labels and helper text */\n.w3a-field-label {\n font-size: 12px;\n color: color-mix(in srgb, var(--w3a-colors-textSecondary), var(--w3a-colors-textPrimary) 35%);\n margin: 6px 2px 6px;\n font-weight: 600;\n}\n\n.w3a-seg-help {\n font-size: 12px;\n color: color-mix(in srgb, var(--w3a-colors-textSecondary), var(--w3a-colors-textPrimary) 25%);\n margin: 0;\n margin-left: 0.75rem;\n}\n\n.w3a-seg-help-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-top: 8px;\n}\n\n/* Tooltip for account existence status */\n.w3a-input-wrap .w3a-tooltip {\n position: absolute;\n right: 0;\n padding: 4px 8px;\n border-radius: 1rem;\n font-size: 0.7rem;\n background: var(--w3a-colors-surface2);\n color: var(--w3a-colors-textPrimary);\n opacity: 0;\n transform: translateX(50px) scale(0.9);\n pointer-events: none;\n transition: opacity 180ms ease, transform 200ms ease;\n z-index: 2;\n}\n\n.w3a-input-wrap .w3a-tooltip.is-visible {\n opacity: 0.8;\n transform: translateX(0px) scale(1);\n}\n\n.w3a-input-wrap .w3a-tooltip.is-error {\n color: var(--w3a-colors-error);\n background: var(--w3a-colors-colorBackground);\n /* background: color-mix(in srgb, var(--w3a-colors-error), transparent 90%); */\n}\n\n.w3a-input-wrap .w3a-tooltip.is-success {\n color: var(--w3a-colors-blue400);\n background: var(--w3a-colors-colorBackground);\n /* background: color-mix(in srgb, var(--w3a-colors-blue400), transparent 90%); */\n}\n\n@keyframes w3a-spin {\n from {\n transform: rotate(0deg);\n }\n\n to {\n transform: rotate(360deg);\n }\n}\n\n@keyframes w3a-ellipsis-dot {\n\n 0%,\n 80%,\n 100% {\n opacity: 0;\n }\n\n 40% {\n opacity: 1;\n }\n}\n\n.w3a-ellipsis {\n display: inline-block;\n}\n\n.w3a-ellipsis-dot {\n display: inline-block;\n opacity: 0;\n animation: w3a-ellipsis-dot 1.2s infinite;\n}\n\n.w3a-ellipsis-dot:nth-child(2) {\n animation-delay: 0.15s;\n}\n\n.w3a-ellipsis-dot:nth-child(3) {\n animation-delay: 0.3s;\n}\n\n/* Pop/bounce-in animation (kept for legacy classes) */\n@keyframes w3a-input-msg-pop {\n 0% {\n opacity: 0;\n transform: translateY(8px) scale(0.98);\n }\n\n 60% {\n opacity: 1;\n transform: translateY(-2px) scale(1.02);\n }\n\n 100% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .w3a-input-msg.is-error {\n animation: none;\n }\n\n .w3a-ellipsis-dot {\n animation: none;\n opacity: 1;\n }\n}\n\n/* Button System */\n.w3a-scan-device-row {}\n\n.w3a-secondary-actions {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.w3a-link-device-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n overflow: hidden;\n gap: 0.5rem;\n padding: 0.5rem 1rem;\n height: 48px;\n width: 100%;\n max-width: 100%;\n background: var(--w3a-colors-surface) !important;\n border: 1px solid var(--w3a-colors-borderPrimary);\n border-radius: 2rem;\n cursor: pointer;\n color: var(--w3a-colors-textPrimary);\n font-family: var(--w3a-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);\n font-weight: 500;\n font-size: 0.875rem;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Hover/focus elevated state (theme-reactive via CSS vars) */\n.w3a-link-device-btn:hover:not(:disabled),\n.w3a-link-device-btn:focus-visible {\n background: var(--w3a-colors-surface2) !important;\n}\n\n.w3a-link-device-btn-primary {\n background: var(--w3a-colors-buttonBackground, var(--w3a-colors-primary, #3b82f6)) !important;\n border-color: transparent;\n border-radius: 2rem;\n color: var(--w3a-colors-textButton, white);\n}\n\n.w3a-link-device-btn-primary:hover:not(:disabled),\n.w3a-link-device-btn-primary:focus-visible {\n background: var(--w3a-colors-buttonHoverBackground, var(--w3a-colors-primaryHover, #2563eb)) !important;\n color: var(--w3a-colors-textButton, white);\n}\n\n.w3a-link-device-btn-primary:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* Button-local spinner: match text size inside buttons */\n.w3a-link-device-btn .w3a-spinner {\n width: 1em;\n height: 1em;\n border-width: 2px;\n}\n\n.w3a-link-device-btn-primary .w3a-spinner {\n border-color: rgba(255, 255, 255, 0.35);\n border-top-color: #ffffff;\n}\n\n/* Email recovery slide */\n.w3a-email-recovery-content {\n width: 100%;\n padding-top: 1rem;\n}\n\n.w3a-email-recovery-slide {\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-height: 260px;\n animation: content-enter 240ms ease-out;\n animation-fill-mode: both;\n}\n\n.w3a-email-recovery-title {\n font-size: 20px;\n font-weight: 700;\n margin: 0.5rem 0.5rem;\n display: flex;\n justify-content: center;\n}\n\n.w3a-email-recovery-help {\n font-size: 0.95rem;\n line-height: 1.2;\n color: color-mix(in srgb, var(--w3a-colors-textSecondary), var(--w3a-colors-textPrimary) 20%);\n margin: 0 0.25rem;\n}\n\n.w3a-email-recovery-meta {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n padding: 0 0.25rem;\n font-size: 0.85rem;\n}\n\n.w3a-email-recovery-meta-label {\n opacity: 0.85;\n}\n\n.w3a-email-recovery-meta-value {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 0.85rem;\n opacity: 0.9;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 70%;\n text-align: right;\n}\n\n.w3a-email-recovery-input-pill {\n height: 54px;\n padding: 0 1rem;\n border: 1px solid var(--w3a-colors-borderPrimary);\n background: var(--w3a-colors-surface);\n transition: box-shadow 160ms ease, border-color 160ms ease;\n}\n\n.w3a-email-recovery-input-pill:focus-within {\n border-color: var(--w3a-colors-primary, #3b82f6);\n}\n\n.w3a-email-recovery-actions {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 4px;\n}\n\n.w3a-email-recovery-summary {\n font-size: 0.875rem;\n line-height: 1.2;\n padding: 0 0.25rem;\n color: color-mix(in srgb, var(--w3a-colors-textSecondary), var(--w3a-colors-textPrimary) 20%);\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.w3a-email-recovery-warning {\n color: color-mix(in srgb, var(--w3a-colors-error), var(--w3a-colors-textPrimary) 30%);\n}\n\n.w3a-email-recovery-from-warning {\n margin-top: 1rem;\n font-weight: 600;\n}\n\n[data-w3a-theme=\"light\"] .w3a-email-recovery-from-warning {\n color: #d80;\n}\n\n[data-w3a-theme=\"dark\"] .w3a-email-recovery-from-warning {\n color: #ea5;\n}\n\n.w3a-email-recovery-link {\n font-size: 0.875rem;\n color: var(--w3a-colors-primary, #3b82f6);\n text-decoration: none;\n padding: 0;\n}\n\n.w3a-email-recovery-link:hover {\n text-decoration: underline;\n}\n\n.w3a-email-recovery-status {\n font-size: 0.875rem;\n padding: 0.75rem 0.875rem;\n border-radius: 1rem;\n border: 1px solid var(--w3a-colors-borderPrimary);\n background: var(--w3a-colors-surface2);\n color: var(--w3a-colors-textPrimary);\n margin-bottom: 4px;\n}\n\n.w3a-email-recovery-status.is-error {\n border-color: color-mix(in srgb, var(--w3a-colors-error), var(--w3a-colors-borderPrimary) 60%);\n color: var(--w3a-colors-error);\n}\n\n.w3a-email-recovery-elapsed {\n margin-left: 6px;\n opacity: 0.75;\n}\n\n.w3a-email-recovery-saved-emails {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.w3a-email-recovery-email-chip {\n border: 1px solid var(--w3a-colors-borderPrimary);\n background: var(--w3a-colors-surface);\n color: var(--w3a-colors-textPrimary);\n padding: 6px 10px;\n border-radius: 999px;\n font-size: 0.85rem;\n cursor: pointer;\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.w3a-email-recovery-email-chip-static {\n cursor: default;\n}\n\n.w3a-email-recovery-email-chip:hover:not(:disabled) {\n background: var(--w3a-colors-surface2);\n}\n\n.w3a-email-recovery-email-chip:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.w3a-email-recovery-toast {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0.75rem 0.875rem;\n border-radius: 1rem;\n border: 1px solid var(--w3a-colors-borderPrimary);\n background: var(--w3a-colors-surface);\n font-size: 0.875rem;\n color: var(--w3a-colors-textPrimary);\n}\n\n.w3a-email-recovery-toast a {\n color: var(--w3a-colors-primary, #3b82f6);\n text-decoration: none;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 100%;\n}\n\n.w3a-email-recovery-toast a:hover {\n text-decoration: underline;\n}\n\n.w3a-email-recovery-toast-close {\n margin-left: auto;\n width: 28px;\n height: 28px;\n border-radius: 999px;\n border: none;\n background: transparent;\n color: var(--w3a-colors-textSecondary);\n cursor: pointer;\n display: grid;\n place-items: center;\n}\n\n.w3a-email-recovery-toast-close:hover {\n background: var(--w3a-colors-surface2);\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA"}
@@ -1,12 +1,11 @@
1
1
  import { SegmentedControl } from "./ui/SegmentedControl.js";
2
- import "./PasskeyAuthMenu.js";
3
- import { AuthMenuMode } from "./authMenuTypes.js";
4
2
  import { ArrowLeftIcon } from "./ui/icons/ArrowLeft.js";
5
3
  import { MailIcon } from "./ui/icons/Mail.js";
4
+ import QRCodeIcon_default from "../QRCodeIcon.js";
5
+ import { AuthMenuMode } from "./authMenuTypes.js";
6
6
  import { PasskeyInput } from "./ui/PasskeyInput.js";
7
7
  import { ContentSwitcher } from "./ui/ContentSwitcher.js";
8
8
  import { EmailRecoverySlide } from "./ui/EmailRecoverySlide.js";
9
- import QRCodeIcon_default from "../QRCodeIcon.js";
10
9
  import { usePasskeyAuthMenuRuntime } from "./adapters/tatchi.js";
11
10
  import { usePasskeyAuthMenuController } from "./controller/usePasskeyAuthMenuController.js";
12
11
  import { useSDKEvents } from "./controller/useSDKEvents.js";
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["PasskeyAuthMenuClient: React.FC<PasskeyAuthMenuProps>","QRCodeIcon"],"sources":["../../../../../src/react/components/PasskeyAuthMenu/client.tsx"],"sourcesContent":["import React from 'react';\nimport { ArrowLeftIcon, MailIcon } from './ui/icons';\nimport { SegmentedControl } from './ui/SegmentedControl';\nimport { PasskeyInput } from './ui/PasskeyInput';\nimport { ContentSwitcher } from './ui/ContentSwitcher';\nimport { EmailRecoverySlide } from './ui/EmailRecoverySlide';\nimport QRCodeIcon from '../QRCodeIcon';\nimport { AuthMenuMode, type PasskeyAuthMenuProps } from './types';\nimport './PasskeyAuthMenu.css';\nimport { usePasskeyAuthMenuRuntime } from './adapters/tatchi';\nimport { usePasskeyAuthMenuController } from './controller/usePasskeyAuthMenuController';\nimport { useSDKEvents } from './controller/useSDKEvents';\n\ntype CSSVarStyle = React.CSSProperties & {\n [key: `--${string}`]: string | number | undefined;\n};\n\nconst LazyShowQRCode = React.lazy(() =>\n import('../ShowQRCode').then((m) => ({ default: m.ShowQRCode })),\n);\n\nconst preloadShowQRCode = () => import('../ShowQRCode').then(() => undefined);\n\nexport const PasskeyAuthMenuClient: React.FC<PasskeyAuthMenuProps> = ({\n onLogin,\n onRegister,\n onSyncAccount,\n linkDeviceOptions,\n emailRecoveryOptions,\n header,\n defaultMode,\n style,\n className,\n socialLogin,\n loadingScreenDelayMs,\n headings,\n showSDKEvents = false,\n}) => {\n const runtime = usePasskeyAuthMenuRuntime();\n const { withSdkEventsHandler } = useSDKEvents({ sdkFlow: runtime.sdkFlow });\n\n const onLoginWithSDKEvents = React.useMemo(\n () => withSdkEventsHandler('login', onLogin, 60_000),\n [onLogin, withSdkEventsHandler],\n );\n const onRegisterWithSDKEvents = React.useMemo(\n () => withSdkEventsHandler('register', onRegister, 90_000),\n [onRegister, withSdkEventsHandler],\n );\n const onSyncWithSDKEvents = React.useMemo(\n () => withSdkEventsHandler('sync', onSyncAccount, 120_000),\n [onSyncAccount, withSdkEventsHandler],\n );\n\n const controller = usePasskeyAuthMenuController(\n {\n onLogin: onLoginWithSDKEvents,\n onRegister: onRegisterWithSDKEvents,\n onSyncAccount: onSyncWithSDKEvents,\n defaultMode,\n headings,\n linkDeviceOptions,\n },\n runtime,\n );\n\n const prefetchQRCode = React.useCallback(() => {\n void preloadShowQRCode().catch(() => {});\n }, []);\n\n const segActiveBg = 'var(--w3a-passkey-auth-menu2-seg-active-bg)';\n\n const rootStyle = React.useMemo<CSSVarStyle>(\n () => ({\n ...style,\n ...(loadingScreenDelayMs != null ? { '--w3a-waiting-delay': `${loadingScreenDelayMs}ms` } : null),\n }),\n [loadingScreenDelayMs, style],\n );\n\n const waitingSDKEventsText = React.useMemo(() => {\n if (!showSDKEvents) return '';\n if (\n controller.mode !== AuthMenuMode.Register &&\n controller.mode !== AuthMenuMode.Login &&\n controller.mode !== AuthMenuMode.Sync\n ) {\n return '';\n }\n const text = runtime.sdkFlow.eventsText?.trim() ?? '';\n if (text.length > 0) {\n const lastLine = text.split('\\n').filter(Boolean).slice(-1)[0] ?? '';\n return lastLine;\n }\n return controller.waiting ? 'Awaiting SDK events…' : '';\n }, [controller.mode, controller.waiting, runtime.sdkFlow.eventsText, showSDKEvents]);\n\n return (\n <div\n className={`w3a-signup-menu-root${className ? ` ${className}` : ''}`}\n data-mode={controller.mode}\n data-waiting={controller.waiting}\n data-scan-device={controller.showScanDevice}\n data-email-recovery={controller.showEmailRecovery}\n style={rootStyle}\n >\n <ContentSwitcher\n waiting={controller.waiting}\n waitingText={\n controller.mode === AuthMenuMode.Register\n ? 'Registering passkey…'\n : controller.mode === AuthMenuMode.Sync\n ? 'Syncing account…'\n : 'Waiting for Passkey…'\n }\n waitingSDKEventsText={waitingSDKEventsText}\n backButton={\n <button\n aria-label=\"Back\"\n onClick={() => {\n if (controller.showEmailRecovery) {\n controller.closeEmailRecovery();\n return;\n }\n controller.onResetToStart();\n }}\n className={`w3a-back-button${controller.waiting || controller.showScanDevice || controller.showEmailRecovery ? ' is-visible' : ''}`}\n >\n <ArrowLeftIcon size={18} strokeWidth={2.25} style={{ display: 'block' }} />\n </button>\n }\n showScanDevice={controller.showScanDevice}\n showQRCodeElement={\n <React.Suspense fallback={<div className=\"qr-loading\"><p>Loading QR…</p></div>}>\n <LazyShowQRCode\n isOpen={controller.linkDevice.isOpen}\n onClose={controller.linkDevice.onClose}\n onEvent={controller.linkDevice.onEvent}\n onError={controller.linkDevice.onError}\n />\n </React.Suspense>\n }\n showEmailRecovery={controller.showEmailRecovery}\n emailRecoveryElement={\n <EmailRecoverySlide\n tatchiPasskey={runtime.tatchiPasskey}\n accountId={runtime.targetAccountId}\n refreshLoginState={runtime.refreshLoginState}\n emailRecoveryOptions={emailRecoveryOptions}\n />\n }\n >\n <div className=\"w3a-header\">\n {header ?? (\n <div>\n <div className=\"w3a-title\">{controller.title.title}</div>\n <div className=\"w3a-subhead\">{controller.title.subtitle}</div>\n </div>\n )}\n </div>\n\n <PasskeyInput\n value={controller.currentValue}\n onChange={controller.onInputChange}\n placeholder={\n controller.mode === AuthMenuMode.Register\n ? 'Pick a username'\n : controller.mode === AuthMenuMode.Sync\n ? 'Leave blank to discover accounts'\n : 'Enter your username'\n }\n postfixText={controller.postfixText}\n isUsingExistingAccount={controller.isUsingExistingAccount}\n canProceed={controller.canShowContinue}\n onProceed={controller.onProceed}\n mode={controller.mode}\n secure={controller.secure}\n waiting={controller.waiting}\n />\n\n <SegmentedControl\n items={[\n { value: AuthMenuMode.Register, label: 'Register', className: 'register' },\n { value: AuthMenuMode.Login, label: 'Login', className: 'login' },\n { value: AuthMenuMode.Sync, label: 'Sync', className: 'sync' },\n ]}\n value={controller.mode}\n onValueChange={(v) => controller.onSegmentChange(v as AuthMenuMode)}\n activeBg={segActiveBg}\n />\n\n <div className=\"w3a-seg-help-row\">\n <div className=\"w3a-seg-help\" aria-live=\"polite\">\n {controller.mode === AuthMenuMode.Login && 'Sign in with your passkey'}\n {controller.mode === AuthMenuMode.Register && 'Create a new account'}\n {controller.mode === AuthMenuMode.Sync && 'Sync account (iCloud/Chrome sync)'}\n </div>\n </div>\n\n <div className=\"w3a-scan-device-row\">\n <div className=\"w3a-section-divider\">\n <span className=\"w3a-section-divider-text\">Already have an account?</span>\n </div>\n <div className=\"w3a-secondary-actions\">\n <button\n onClick={controller.openScanDevice}\n onPointerEnter={prefetchQRCode}\n onFocus={prefetchQRCode}\n onTouchStart={prefetchQRCode}\n className=\"w3a-link-device-btn\"\n >\n <QRCodeIcon width={18} height={18} strokeWidth={2} />\n Scan and Link Device\n </button>\n <button\n onClick={controller.openEmailRecovery}\n className=\"w3a-link-device-btn\"\n >\n <MailIcon size={18} strokeWidth={2} style={{ display: 'block' }} />\n Recover Account with Email\n </button>\n </div>\n </div>\n </ContentSwitcher>\n </div>\n );\n};\n\nexport default PasskeyAuthMenuClient;\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["PasskeyAuthMenuClient: React.FC<PasskeyAuthMenuProps>","QRCodeIcon"],"sources":["../../../../../src/react/components/PasskeyAuthMenu/client.tsx"],"sourcesContent":["import React from 'react';\nimport { ArrowLeftIcon, MailIcon } from './ui/icons';\nimport { SegmentedControl } from './ui/SegmentedControl';\nimport { PasskeyInput } from './ui/PasskeyInput';\nimport { ContentSwitcher } from './ui/ContentSwitcher';\nimport { EmailRecoverySlide } from './ui/EmailRecoverySlide';\nimport QRCodeIcon from '../QRCodeIcon';\nimport { AuthMenuMode, type PasskeyAuthMenuProps } from './types';\nimport { usePasskeyAuthMenuRuntime } from './adapters/tatchi';\nimport { usePasskeyAuthMenuController } from './controller/usePasskeyAuthMenuController';\nimport { useSDKEvents } from './controller/useSDKEvents';\n\ntype CSSVarStyle = React.CSSProperties & {\n [key: `--${string}`]: string | number | undefined;\n};\n\nconst LazyShowQRCode = React.lazy(() =>\n import('../ShowQRCode').then((m) => ({ default: m.ShowQRCode })),\n);\n\nconst preloadShowQRCode = () => import('../ShowQRCode').then(() => undefined);\n\nexport const PasskeyAuthMenuClient: React.FC<PasskeyAuthMenuProps> = ({\n onLogin,\n onRegister,\n onSyncAccount,\n linkDeviceOptions,\n emailRecoveryOptions,\n header,\n defaultMode,\n style,\n className,\n socialLogin,\n loadingScreenDelayMs,\n headings,\n showSDKEvents = false,\n}) => {\n const runtime = usePasskeyAuthMenuRuntime();\n const { withSdkEventsHandler } = useSDKEvents({ sdkFlow: runtime.sdkFlow });\n\n const onLoginWithSDKEvents = React.useMemo(\n () => withSdkEventsHandler('login', onLogin, 60_000),\n [onLogin, withSdkEventsHandler],\n );\n const onRegisterWithSDKEvents = React.useMemo(\n () => withSdkEventsHandler('register', onRegister, 90_000),\n [onRegister, withSdkEventsHandler],\n );\n const onSyncWithSDKEvents = React.useMemo(\n () => withSdkEventsHandler('sync', onSyncAccount, 120_000),\n [onSyncAccount, withSdkEventsHandler],\n );\n\n const controller = usePasskeyAuthMenuController(\n {\n onLogin: onLoginWithSDKEvents,\n onRegister: onRegisterWithSDKEvents,\n onSyncAccount: onSyncWithSDKEvents,\n defaultMode,\n headings,\n linkDeviceOptions,\n },\n runtime,\n );\n\n const prefetchQRCode = React.useCallback(() => {\n void preloadShowQRCode().catch(() => { });\n }, []);\n\n const segActiveBg = 'var(--w3a-passkey-auth-menu2-seg-active-bg)';\n\n const rootStyle = React.useMemo<CSSVarStyle>(\n () => ({\n ...style,\n ...(loadingScreenDelayMs != null ? { '--w3a-waiting-delay': `${loadingScreenDelayMs}ms` } : null),\n }),\n [loadingScreenDelayMs, style],\n );\n\n const waitingSDKEventsText = React.useMemo(() => {\n if (!showSDKEvents) return '';\n if (\n controller.mode !== AuthMenuMode.Register &&\n controller.mode !== AuthMenuMode.Login &&\n controller.mode !== AuthMenuMode.Sync\n ) {\n return '';\n }\n const text = runtime.sdkFlow.eventsText?.trim() ?? '';\n if (text.length > 0) {\n const lastLine = text.split('\\n').filter(Boolean).slice(-1)[0] ?? '';\n return lastLine;\n }\n return controller.waiting ? 'Awaiting SDK events…' : '';\n }, [controller.mode, controller.waiting, runtime.sdkFlow.eventsText, showSDKEvents]);\n\n return (\n <div\n className={`w3a-signup-menu-root${className ? ` ${className}` : ''}`}\n data-mode={controller.mode}\n data-waiting={controller.waiting}\n data-scan-device={controller.showScanDevice}\n data-email-recovery={controller.showEmailRecovery}\n style={rootStyle}\n >\n <ContentSwitcher\n waiting={controller.waiting}\n waitingText={\n controller.mode === AuthMenuMode.Register\n ? 'Registering passkey…'\n : controller.mode === AuthMenuMode.Sync\n ? 'Syncing account…'\n : 'Waiting for Passkey…'\n }\n waitingSDKEventsText={waitingSDKEventsText}\n backButton={\n <button\n aria-label=\"Back\"\n onClick={() => {\n if (controller.showEmailRecovery) {\n controller.closeEmailRecovery();\n return;\n }\n controller.onResetToStart();\n }}\n className={`w3a-back-button${controller.waiting || controller.showScanDevice || controller.showEmailRecovery ? ' is-visible' : ''}`}\n >\n <ArrowLeftIcon size={18} strokeWidth={2.25} style={{ display: 'block' }} />\n </button>\n }\n showScanDevice={controller.showScanDevice}\n showQRCodeElement={\n <React.Suspense fallback={<div className=\"qr-loading\"><p>Loading QR…</p></div>}>\n <LazyShowQRCode\n isOpen={controller.linkDevice.isOpen}\n onClose={controller.linkDevice.onClose}\n onEvent={controller.linkDevice.onEvent}\n onError={controller.linkDevice.onError}\n />\n </React.Suspense>\n }\n showEmailRecovery={controller.showEmailRecovery}\n emailRecoveryElement={\n <EmailRecoverySlide\n tatchiPasskey={runtime.tatchiPasskey}\n accountId={runtime.targetAccountId}\n refreshLoginState={runtime.refreshLoginState}\n emailRecoveryOptions={emailRecoveryOptions}\n />\n }\n >\n <div className=\"w3a-header\">\n {header ?? (\n <div>\n <div className=\"w3a-title\">{controller.title.title}</div>\n <div className=\"w3a-subhead\">{controller.title.subtitle}</div>\n </div>\n )}\n </div>\n\n <PasskeyInput\n value={controller.currentValue}\n onChange={controller.onInputChange}\n placeholder={\n controller.mode === AuthMenuMode.Register\n ? 'Pick a username'\n : controller.mode === AuthMenuMode.Sync\n ? 'Leave blank to discover accounts'\n : 'Enter your username'\n }\n postfixText={controller.postfixText}\n isUsingExistingAccount={controller.isUsingExistingAccount}\n canProceed={controller.canShowContinue}\n onProceed={controller.onProceed}\n mode={controller.mode}\n secure={controller.secure}\n waiting={controller.waiting}\n />\n\n <SegmentedControl\n items={[\n { value: AuthMenuMode.Register, label: 'Register', className: 'register' },\n { value: AuthMenuMode.Login, label: 'Login', className: 'login' },\n { value: AuthMenuMode.Sync, label: 'Sync', className: 'sync' },\n ]}\n value={controller.mode}\n onValueChange={(v) => controller.onSegmentChange(v as AuthMenuMode)}\n activeBg={segActiveBg}\n />\n\n <div className=\"w3a-seg-help-row\">\n <div className=\"w3a-seg-help\" aria-live=\"polite\">\n {controller.mode === AuthMenuMode.Login && 'Sign in with your passkey'}\n {controller.mode === AuthMenuMode.Register && 'Create a new account'}\n {controller.mode === AuthMenuMode.Sync && 'Sync account (iCloud/Chrome sync)'}\n </div>\n </div>\n\n <div className=\"w3a-scan-device-row\">\n <div className=\"w3a-section-divider\">\n <span className=\"w3a-section-divider-text\">Already have an account?</span>\n </div>\n <div className=\"w3a-secondary-actions\">\n <button\n onClick={controller.openScanDevice}\n onPointerEnter={prefetchQRCode}\n onFocus={prefetchQRCode}\n onTouchStart={prefetchQRCode}\n className=\"w3a-link-device-btn\"\n >\n <QRCodeIcon width={18} height={18} strokeWidth={2} />\n Scan and Link Device\n </button>\n <button\n onClick={controller.openEmailRecovery}\n className=\"w3a-link-device-btn\"\n >\n <MailIcon size={18} strokeWidth={2} style={{ display: 'block' }} />\n Recover Account with Email\n </button>\n </div>\n </div>\n </ContentSwitcher>\n </div>\n );\n};\n\nexport default PasskeyAuthMenuClient;\n"],"mappings":"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"}
@@ -35,9 +35,13 @@ function getModeTitle(mode, headings) {
35
35
  * - No IndexedDB/wallet-prefill logic yet (intentionally).
36
36
  * - Pure state transitions to keep the baseline bundle small and predictable.
37
37
  */
38
- function useAuthMenuMode({ defaultMode, accountExists, currentValue, setCurrentValue, headings }) {
38
+ function useAuthMenuMode({ defaultMode, accountExists, currentValue, setCurrentValue, headings, forceInitialRegister = false }) {
39
39
  const preferredDefaultMode = resolveDefaultMode(accountExists, defaultMode);
40
- const [mode, setMode] = React.useState(preferredDefaultMode);
40
+ const [mode, setMode] = React.useState(() => {
41
+ if (typeof defaultMode === "number") return defaultMode;
42
+ if (forceInitialRegister) return AuthMenuMode.Register;
43
+ return preferredDefaultMode;
44
+ });
41
45
  const title = React.useMemo(() => getModeTitle(mode, headings), [mode, headings]);
42
46
  const onSegmentChange = (nextMode) => {
43
47
  setMode(nextMode);
@@ -61,5 +65,5 @@ function useAuthMenuMode({ defaultMode, accountExists, currentValue, setCurrentV
61
65
  }
62
66
 
63
67
  //#endregion
64
- export { useAuthMenuMode };
68
+ export { getModeTitle, useAuthMenuMode };
65
69
  //# sourceMappingURL=mode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mode.js","names":["defaults: Record<AuthMenuMode, AuthMenuTitle>"],"sources":["../../../../../../src/react/components/PasskeyAuthMenu/controller/mode.ts"],"sourcesContent":["import React from 'react';\nimport type { AuthMenuHeadings } from '../types';\nimport { AuthMenuMode } from '../types';\n\nexport interface AuthMenuTitle {\n title: string;\n subtitle: string;\n}\n\nexport function resolveDefaultMode(\n accountExists: boolean,\n requested?: AuthMenuMode | null,\n): AuthMenuMode {\n if (typeof requested === 'number') return requested;\n return accountExists ? AuthMenuMode.Login : AuthMenuMode.Register;\n}\n\nexport function getModeTitle(mode: AuthMenuMode, headings?: AuthMenuHeadings | null): AuthMenuTitle {\n const defaults: Record<AuthMenuMode, AuthMenuTitle> = {\n [AuthMenuMode.Login]: { title: 'Login', subtitle: 'Login with Passkey' },\n [AuthMenuMode.Register]: { title: 'Register Account', subtitle: 'Create a wallet with Passkey' },\n [AuthMenuMode.Sync]: { title: 'Sync Account', subtitle: 'Sync a wallet to this device with Passkey' },\n } as const;\n\n if (headings) {\n if (mode === AuthMenuMode.Login && headings.login) return headings.login;\n if (mode === AuthMenuMode.Register && headings.registration) return headings.registration;\n if (mode === AuthMenuMode.Sync && headings.syncAccount) return headings.syncAccount;\n }\n\n return defaults[mode] ?? defaults[AuthMenuMode.Login];\n}\n\nexport interface UseAuthMenuModeArgs {\n defaultMode?: AuthMenuMode;\n accountExists: boolean;\n currentValue: string;\n setCurrentValue: (v: string) => void;\n headings?: AuthMenuHeadings | null;\n}\n\nexport interface UseAuthMenuModeResult {\n mode: AuthMenuMode;\n setMode: React.Dispatch<React.SetStateAction<AuthMenuMode>>;\n title: AuthMenuTitle;\n onSegmentChange: (next: AuthMenuMode) => void;\n onInputChange: (val: string) => void;\n resetToDefault: () => void;\n}\n\n/**\n * `useAuthMenuMode`\n *\n * Minimal mode/title controller for PasskeyAuthMenu.\n * - No IndexedDB/wallet-prefill logic yet (intentionally).\n * - Pure state transitions to keep the baseline bundle small and predictable.\n */\nexport function useAuthMenuMode({\n defaultMode,\n accountExists,\n currentValue,\n setCurrentValue,\n headings,\n}: UseAuthMenuModeArgs): UseAuthMenuModeResult {\n const preferredDefaultMode = resolveDefaultMode(accountExists, defaultMode);\n const [mode, setMode] = React.useState<AuthMenuMode>(preferredDefaultMode);\n const title = React.useMemo(() => getModeTitle(mode, headings), [mode, headings]);\n\n const onSegmentChange = (nextMode: AuthMenuMode) => {\n setMode(nextMode);\n };\n\n const onInputChange = (val: string) => {\n setCurrentValue(val);\n };\n\n const resetToDefault = () => {\n const nextMode = resolveDefaultMode(accountExists, defaultMode);\n setMode(nextMode);\n setCurrentValue('');\n };\n\n return { mode, setMode, title, onSegmentChange, onInputChange, resetToDefault };\n}\n\nexport default useAuthMenuMode;\n"],"mappings":";;;;AASA,SAAgB,mBACd,eACA,WACc;AACd,KAAI,OAAO,cAAc,SAAU,QAAO;AAC1C,QAAO,gBAAgB,aAAa,QAAQ,aAAa;;AAG3D,SAAgB,aAAa,MAAoB,UAAmD;CAClG,MAAMA,WAAgD;GACnD,aAAa,QAAQ;GAAE,OAAO;GAAS,UAAU;;GACjD,aAAa,WAAW;GAAE,OAAO;GAAoB,UAAU;;GAC/D,aAAa,OAAO;GAAE,OAAO;GAAgB,UAAU;;;AAG1D,KAAI,UAAU;AACZ,MAAI,SAAS,aAAa,SAAS,SAAS,MAAO,QAAO,SAAS;AACnE,MAAI,SAAS,aAAa,YAAY,SAAS,aAAc,QAAO,SAAS;AAC7E,MAAI,SAAS,aAAa,QAAQ,SAAS,YAAa,QAAO,SAAS;;AAG1E,QAAO,SAAS,SAAS,SAAS,aAAa;;;;;;;;;AA2BjD,SAAgB,gBAAgB,EAC9B,aACA,eACA,cACA,iBACA,YAC6C;CAC7C,MAAM,uBAAuB,mBAAmB,eAAe;CAC/D,MAAM,CAAC,MAAM,WAAW,MAAM,SAAuB;CACrD,MAAM,QAAQ,MAAM,cAAc,aAAa,MAAM,WAAW,CAAC,MAAM;CAEvE,MAAM,mBAAmB,aAA2B;AAClD,UAAQ;;CAGV,MAAM,iBAAiB,QAAgB;AACrC,kBAAgB;;CAGlB,MAAM,uBAAuB;EAC3B,MAAM,WAAW,mBAAmB,eAAe;AACnD,UAAQ;AACR,kBAAgB;;AAGlB,QAAO;EAAE;EAAM;EAAS;EAAO;EAAiB;EAAe"}
1
+ {"version":3,"file":"mode.js","names":["defaults: Record<AuthMenuMode, AuthMenuTitle>"],"sources":["../../../../../../src/react/components/PasskeyAuthMenu/controller/mode.ts"],"sourcesContent":["import React from 'react';\nimport type { AuthMenuHeadings } from '../types';\nimport { AuthMenuMode } from '../types';\n\nexport interface AuthMenuTitle {\n title: string;\n subtitle: string;\n}\n\nexport function resolveDefaultMode(\n accountExists: boolean,\n requested?: AuthMenuMode | null,\n): AuthMenuMode {\n if (typeof requested === 'number') return requested;\n return accountExists ? AuthMenuMode.Login : AuthMenuMode.Register;\n}\n\nexport function getModeTitle(mode: AuthMenuMode, headings?: AuthMenuHeadings | null): AuthMenuTitle {\n const defaults: Record<AuthMenuMode, AuthMenuTitle> = {\n [AuthMenuMode.Login]: { title: 'Login', subtitle: 'Login with Passkey' },\n [AuthMenuMode.Register]: { title: 'Register Account', subtitle: 'Create a wallet with Passkey' },\n [AuthMenuMode.Sync]: { title: 'Sync Account', subtitle: 'Sync a wallet to this device with Passkey' },\n } as const;\n\n if (headings) {\n if (mode === AuthMenuMode.Login && headings.login) return headings.login;\n if (mode === AuthMenuMode.Register && headings.registration) return headings.registration;\n if (mode === AuthMenuMode.Sync && headings.syncAccount) return headings.syncAccount;\n }\n\n return defaults[mode] ?? defaults[AuthMenuMode.Login];\n}\n\nexport interface UseAuthMenuModeArgs {\n defaultMode?: AuthMenuMode;\n accountExists: boolean;\n currentValue: string;\n setCurrentValue: (v: string) => void;\n headings?: AuthMenuHeadings | null;\n /**\n * When true, forces the initial client render to start in Register mode, even if\n * `accountExists` suggests Login. This is used to align hydration with the shell skeleton.\n */\n forceInitialRegister?: boolean;\n}\n\nexport interface UseAuthMenuModeResult {\n mode: AuthMenuMode;\n setMode: React.Dispatch<React.SetStateAction<AuthMenuMode>>;\n title: AuthMenuTitle;\n onSegmentChange: (next: AuthMenuMode) => void;\n onInputChange: (val: string) => void;\n resetToDefault: () => void;\n}\n\n/**\n * `useAuthMenuMode`\n *\n * Minimal mode/title controller for PasskeyAuthMenu.\n * - No IndexedDB/wallet-prefill logic yet (intentionally).\n * - Pure state transitions to keep the baseline bundle small and predictable.\n */\nexport function useAuthMenuMode({\n defaultMode,\n accountExists,\n currentValue,\n setCurrentValue,\n headings,\n forceInitialRegister = false,\n}: UseAuthMenuModeArgs): UseAuthMenuModeResult {\n const preferredDefaultMode = resolveDefaultMode(accountExists, defaultMode);\n const [mode, setMode] = React.useState<AuthMenuMode>(() => {\n if (typeof defaultMode === 'number') return defaultMode;\n if (forceInitialRegister) return AuthMenuMode.Register;\n return preferredDefaultMode;\n });\n const title = React.useMemo(() => getModeTitle(mode, headings), [mode, headings]);\n\n const onSegmentChange = (nextMode: AuthMenuMode) => {\n setMode(nextMode);\n };\n\n const onInputChange = (val: string) => {\n setCurrentValue(val);\n };\n\n const resetToDefault = () => {\n const nextMode = resolveDefaultMode(accountExists, defaultMode);\n setMode(nextMode);\n setCurrentValue('');\n };\n\n return { mode, setMode, title, onSegmentChange, onInputChange, resetToDefault };\n}\n\nexport default useAuthMenuMode;\n"],"mappings":";;;;AASA,SAAgB,mBACd,eACA,WACc;AACd,KAAI,OAAO,cAAc,SAAU,QAAO;AAC1C,QAAO,gBAAgB,aAAa,QAAQ,aAAa;;AAG3D,SAAgB,aAAa,MAAoB,UAAmD;CAClG,MAAMA,WAAgD;GACnD,aAAa,QAAQ;GAAE,OAAO;GAAS,UAAU;;GACjD,aAAa,WAAW;GAAE,OAAO;GAAoB,UAAU;;GAC/D,aAAa,OAAO;GAAE,OAAO;GAAgB,UAAU;;;AAG1D,KAAI,UAAU;AACZ,MAAI,SAAS,aAAa,SAAS,SAAS,MAAO,QAAO,SAAS;AACnE,MAAI,SAAS,aAAa,YAAY,SAAS,aAAc,QAAO,SAAS;AAC7E,MAAI,SAAS,aAAa,QAAQ,SAAS,YAAa,QAAO,SAAS;;AAG1E,QAAO,SAAS,SAAS,SAAS,aAAa;;;;;;;;;AAgCjD,SAAgB,gBAAgB,EAC9B,aACA,eACA,cACA,iBACA,UACA,uBAAuB,SACsB;CAC7C,MAAM,uBAAuB,mBAAmB,eAAe;CAC/D,MAAM,CAAC,MAAM,WAAW,MAAM,eAA6B;AACzD,MAAI,OAAO,gBAAgB,SAAU,QAAO;AAC5C,MAAI,qBAAsB,QAAO,aAAa;AAC9C,SAAO;;CAET,MAAM,QAAQ,MAAM,cAAc,aAAa,MAAM,WAAW,CAAC,MAAM;CAEvE,MAAM,mBAAmB,aAA2B;AAClD,UAAQ;;CAGV,MAAM,iBAAiB,QAAgB;AACrC,kBAAgB;;CAGlB,MAAM,uBAAuB;EAC3B,MAAM,WAAW,mBAAmB,eAAe;AACnD,UAAQ;AACR,kBAAgB;;AAGlB,QAAO;EAAE;EAAM;EAAS;EAAO;EAAiB;EAAe"}
@@ -1,5 +1,6 @@
1
1
  import { AuthMenuMode } from "../authMenuTypes.js";
2
2
  import { useAuthMenuMode } from "./mode.js";
3
+ import { usePasskeyAuthMenuForceInitialRegister } from "../hydrationContext.js";
3
4
  import { getProceedEligibility } from "./proceedEligibility.js";
4
5
  import React from "react";
5
6
 
@@ -8,12 +9,14 @@ function usePasskeyAuthMenuController(props, runtime) {
8
9
  const secure = typeof window !== "undefined" ? window.isSecureContext : true;
9
10
  const currentValue = runtime.inputUsername;
10
11
  const setCurrentValue = runtime.setInputUsername;
12
+ const forceInitialRegister = usePasskeyAuthMenuForceInitialRegister();
11
13
  const { mode, setMode, title, onSegmentChange: onSegmentChangeBase, onInputChange: onInputChangeBase, resetToDefault } = useAuthMenuMode({
12
14
  defaultMode: props.defaultMode,
13
15
  accountExists: runtime.accountExists,
14
16
  currentValue,
15
17
  setCurrentValue,
16
- headings: props.headings
18
+ headings: props.headings,
19
+ forceInitialRegister
17
20
  });
18
21
  const latestValueRef = React.useRef(currentValue);
19
22
  React.useEffect(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"usePasskeyAuthMenuController.js","names":["linkDevice: PasskeyAuthMenuLinkDeviceController"],"sources":["../../../../../../src/react/components/PasskeyAuthMenu/controller/usePasskeyAuthMenuController.ts"],"sourcesContent":["import React from 'react';\nimport type { DeviceLinkingSSEEvent } from '@/core/types/sdkSentEvents';\nimport type { PasskeyAuthMenuRuntime } from '../adapters/tatchi';\nimport { AuthMenuMode, type PasskeyAuthMenuProps } from '../types';\nimport { useAuthMenuMode } from './mode';\nimport { getProceedEligibility } from './proceedEligibility';\n\nexport interface PasskeyAuthMenuLinkDeviceController {\n isOpen: boolean;\n onClose: () => void;\n onEvent: (event: DeviceLinkingSSEEvent) => void;\n onError: (error: Error) => void;\n}\n\nexport interface PasskeyAuthMenuController {\n mode: AuthMenuMode;\n title: { title: string; subtitle: string };\n waiting: boolean;\n showScanDevice: boolean;\n showEmailRecovery: boolean;\n currentValue: string;\n postfixText?: string;\n isUsingExistingAccount?: boolean;\n secure: boolean;\n canShowContinue: boolean;\n canSubmit: boolean;\n onSegmentChange: (next: AuthMenuMode) => void;\n onInputChange: (val: string) => void;\n onProceed: () => void;\n onResetToStart: () => void;\n openScanDevice: () => void;\n openEmailRecovery: () => void;\n closeEmailRecovery: () => void;\n closeLinkDeviceView: (reason: 'user' | 'flow') => void;\n linkDevice: PasskeyAuthMenuLinkDeviceController;\n}\n\nexport function usePasskeyAuthMenuController(\n props: Pick<\n PasskeyAuthMenuProps,\n 'onLogin' | 'onRegister' | 'onSyncAccount' | 'defaultMode' | 'headings' | 'linkDeviceOptions'\n >,\n runtime: PasskeyAuthMenuRuntime,\n): PasskeyAuthMenuController {\n const secure = typeof window !== 'undefined' ? window.isSecureContext : true;\n const currentValue = runtime.inputUsername;\n const setCurrentValue = runtime.setInputUsername;\n\n const { mode, setMode, title, onSegmentChange: onSegmentChangeBase, onInputChange: onInputChangeBase, resetToDefault } = useAuthMenuMode({\n defaultMode: props.defaultMode,\n accountExists: runtime.accountExists,\n currentValue,\n setCurrentValue,\n headings: props.headings,\n });\n\n const latestValueRef = React.useRef<string>(currentValue);\n React.useEffect(() => {\n latestValueRef.current = currentValue;\n }, [currentValue]);\n\n // Recent-login prefill state (from lazy feature island).\n const prefilledFromRecentRef = React.useRef(false);\n const prefilledValueRef = React.useRef<string>('');\n const prevModeRef = React.useRef<AuthMenuMode | null>(null);\n const lastUserSelectedModeRef = React.useRef<AuthMenuMode | null>(null);\n\n const clearPrefillMarkers = React.useCallback(() => {\n prefilledFromRecentRef.current = false;\n prefilledValueRef.current = '';\n }, []);\n\n const onSegmentChange = React.useCallback(\n (next: AuthMenuMode) => {\n lastUserSelectedModeRef.current = next;\n if (mode === AuthMenuMode.Login && next !== AuthMenuMode.Login) {\n if (prefilledFromRecentRef.current && currentValue === prefilledValueRef.current) {\n setCurrentValue('');\n }\n clearPrefillMarkers();\n }\n onSegmentChangeBase(next);\n },\n [mode, currentValue, setCurrentValue, onSegmentChangeBase, clearPrefillMarkers],\n );\n\n const onInputChange = React.useCallback(\n (val: string) => {\n if (val !== prefilledValueRef.current) {\n prefilledFromRecentRef.current = false;\n }\n onInputChangeBase(val);\n },\n [onInputChangeBase],\n );\n\n const { canShowContinue, canSubmit } = getProceedEligibility({\n mode,\n currentValue,\n accountExists: runtime.accountExists,\n secure,\n });\n\n const [waiting, setWaiting] = React.useState(false);\n const [showScanDevice, setShowScanDevice] = React.useState(false);\n const [showEmailRecovery, setShowEmailRecovery] = React.useState(false);\n\n // If the user is attempting to register but we discover the account already exists,\n // automatically switch them to the Login tab.\n React.useEffect(() => {\n if (waiting) return;\n if (mode !== AuthMenuMode.Register) return;\n if (!runtime.accountExists) return;\n if (lastUserSelectedModeRef.current === AuthMenuMode.Register) return;\n setMode(AuthMenuMode.Login);\n }, [mode, runtime.accountExists, setMode, waiting]);\n\n // Lazy feature-island: entering Login can prefill the last used account username.\n React.useEffect(() => {\n const prevMode = prevModeRef.current;\n prevModeRef.current = mode;\n\n const enteringLogin = mode === AuthMenuMode.Login && prevMode !== AuthMenuMode.Login;\n if (!enteringLogin) return;\n if (latestValueRef.current.trim().length > 0) return;\n\n let cancelled = false;\n void import('../features/recentLoginPrefill')\n .then(async (m) => {\n const result = await m.getRecentLoginPrefill(runtime.tatchiPasskey);\n if (cancelled || !result?.username) return;\n if (prevModeRef.current !== AuthMenuMode.Login) return;\n if (latestValueRef.current.trim().length > 0) return;\n\n setCurrentValue(result.username);\n prefilledFromRecentRef.current = true;\n prefilledValueRef.current = result.username;\n })\n .catch(() => {});\n\n return () => {\n cancelled = true;\n };\n }, [mode, runtime.tatchiPasskey, setCurrentValue]);\n\n const fallbackOnEvent = React.useCallback((event: DeviceLinkingSSEEvent) => {\n console.log('ShowQRCode event:', event);\n }, []);\n\n const fallbackOnError = React.useCallback((error: Error) => {\n console.error('ShowQRCode error:', error);\n }, []);\n\n const handleLinkDeviceEvent = props.linkDeviceOptions?.onEvent ?? fallbackOnEvent;\n const handleLinkDeviceError = props.linkDeviceOptions?.onError ?? fallbackOnError;\n const handleLinkDeviceCancelled = props.linkDeviceOptions?.onCancelled;\n\n const stopLinkDeviceFlow = React.useCallback(() => {\n const stopper = runtime.stopDevice2LinkingFlow;\n if (!stopper) return;\n void stopper().catch(() => {});\n }, [runtime.stopDevice2LinkingFlow]);\n\n const closeLinkDeviceView = React.useCallback(\n (reason: 'user' | 'flow') => {\n stopLinkDeviceFlow();\n setShowScanDevice(false);\n if (reason === 'user') {\n handleLinkDeviceCancelled?.();\n }\n },\n [stopLinkDeviceFlow, handleLinkDeviceCancelled],\n );\n\n const onResetToStart = React.useCallback(() => {\n setWaiting(false);\n if (showScanDevice) {\n closeLinkDeviceView('user');\n } else {\n setShowScanDevice(false);\n }\n setShowEmailRecovery(false);\n lastUserSelectedModeRef.current = null;\n resetToDefault();\n setCurrentValue('');\n clearPrefillMarkers();\n }, [showScanDevice, closeLinkDeviceView, resetToDefault, setCurrentValue, clearPrefillMarkers]);\n\n const onProceed = React.useCallback(() => {\n if (!canSubmit) return;\n\n setWaiting(true);\n\n void (async () => {\n try {\n if (mode === AuthMenuMode.Sync) {\n await props.onSyncAccount?.();\n setWaiting(false);\n setMode(AuthMenuMode.Login);\n } else if (mode === AuthMenuMode.Login) {\n await props.onLogin?.();\n setWaiting(false);\n closeLinkDeviceView('flow');\n setMode(AuthMenuMode.Login);\n } else {\n await props.onRegister?.();\n setWaiting(false);\n setMode(AuthMenuMode.Login);\n }\n } catch {\n if (mode === AuthMenuMode.Login) {\n setWaiting(false);\n closeLinkDeviceView('flow');\n setMode(mode);\n return;\n }\n onResetToStart();\n }\n })();\n }, [\n canSubmit,\n mode,\n props.onSyncAccount,\n props.onLogin,\n props.onRegister,\n setMode,\n closeLinkDeviceView,\n onResetToStart,\n ]);\n\n const openScanDevice = React.useCallback(() => {\n setShowEmailRecovery(false);\n setShowScanDevice(true);\n }, []);\n\n const openEmailRecovery = React.useCallback(() => {\n stopLinkDeviceFlow();\n setShowScanDevice(false);\n setShowEmailRecovery(true);\n }, [stopLinkDeviceFlow]);\n\n const closeEmailRecovery = React.useCallback(() => {\n setShowEmailRecovery(false);\n }, []);\n\n const linkDevice: PasskeyAuthMenuLinkDeviceController = React.useMemo(\n () => ({\n isOpen: showScanDevice,\n onClose: () => closeLinkDeviceView('flow'),\n onEvent: handleLinkDeviceEvent,\n onError: handleLinkDeviceError,\n }),\n [showScanDevice, closeLinkDeviceView, handleLinkDeviceEvent, handleLinkDeviceError],\n );\n\n return {\n mode,\n title,\n waiting,\n showScanDevice,\n showEmailRecovery,\n currentValue,\n postfixText: runtime.displayPostfix,\n isUsingExistingAccount: runtime.isUsingExistingAccount,\n secure,\n canShowContinue,\n canSubmit,\n onSegmentChange,\n onInputChange,\n onProceed,\n onResetToStart,\n openScanDevice,\n openEmailRecovery,\n closeEmailRecovery,\n closeLinkDeviceView,\n linkDevice,\n };\n}\n\nexport default usePasskeyAuthMenuController;\n"],"mappings":"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"}
1
+ {"version":3,"file":"usePasskeyAuthMenuController.js","names":["linkDevice: PasskeyAuthMenuLinkDeviceController"],"sources":["../../../../../../src/react/components/PasskeyAuthMenu/controller/usePasskeyAuthMenuController.ts"],"sourcesContent":["import React from 'react';\nimport type { DeviceLinkingSSEEvent } from '@/core/types/sdkSentEvents';\nimport type { PasskeyAuthMenuRuntime } from '../adapters/tatchi';\nimport { AuthMenuMode, type PasskeyAuthMenuProps } from '../types';\nimport { usePasskeyAuthMenuForceInitialRegister } from '../hydrationContext';\nimport { useAuthMenuMode } from './mode';\nimport { getProceedEligibility } from './proceedEligibility';\n\nexport interface PasskeyAuthMenuLinkDeviceController {\n isOpen: boolean;\n onClose: () => void;\n onEvent: (event: DeviceLinkingSSEEvent) => void;\n onError: (error: Error) => void;\n}\n\nexport interface PasskeyAuthMenuController {\n mode: AuthMenuMode;\n title: { title: string; subtitle: string };\n waiting: boolean;\n showScanDevice: boolean;\n showEmailRecovery: boolean;\n currentValue: string;\n postfixText?: string;\n isUsingExistingAccount?: boolean;\n secure: boolean;\n canShowContinue: boolean;\n canSubmit: boolean;\n onSegmentChange: (next: AuthMenuMode) => void;\n onInputChange: (val: string) => void;\n onProceed: () => void;\n onResetToStart: () => void;\n openScanDevice: () => void;\n openEmailRecovery: () => void;\n closeEmailRecovery: () => void;\n closeLinkDeviceView: (reason: 'user' | 'flow') => void;\n linkDevice: PasskeyAuthMenuLinkDeviceController;\n}\n\nexport function usePasskeyAuthMenuController(\n props: Pick<\n PasskeyAuthMenuProps,\n 'onLogin' | 'onRegister' | 'onSyncAccount' | 'defaultMode' | 'headings' | 'linkDeviceOptions'\n >,\n runtime: PasskeyAuthMenuRuntime,\n): PasskeyAuthMenuController {\n const secure = typeof window !== 'undefined' ? window.isSecureContext : true;\n const currentValue = runtime.inputUsername;\n const setCurrentValue = runtime.setInputUsername;\n const forceInitialRegister = usePasskeyAuthMenuForceInitialRegister();\n\n const { mode, setMode, title, onSegmentChange: onSegmentChangeBase, onInputChange: onInputChangeBase, resetToDefault } = useAuthMenuMode({\n defaultMode: props.defaultMode,\n accountExists: runtime.accountExists,\n currentValue,\n setCurrentValue,\n headings: props.headings,\n forceInitialRegister,\n });\n\n const latestValueRef = React.useRef<string>(currentValue);\n React.useEffect(() => {\n latestValueRef.current = currentValue;\n }, [currentValue]);\n\n // Recent-login prefill state (from lazy feature island).\n const prefilledFromRecentRef = React.useRef(false);\n const prefilledValueRef = React.useRef<string>('');\n const prevModeRef = React.useRef<AuthMenuMode | null>(null);\n const lastUserSelectedModeRef = React.useRef<AuthMenuMode | null>(null);\n\n const clearPrefillMarkers = React.useCallback(() => {\n prefilledFromRecentRef.current = false;\n prefilledValueRef.current = '';\n }, []);\n\n const onSegmentChange = React.useCallback(\n (next: AuthMenuMode) => {\n lastUserSelectedModeRef.current = next;\n if (mode === AuthMenuMode.Login && next !== AuthMenuMode.Login) {\n if (prefilledFromRecentRef.current && currentValue === prefilledValueRef.current) {\n setCurrentValue('');\n }\n clearPrefillMarkers();\n }\n onSegmentChangeBase(next);\n },\n [mode, currentValue, setCurrentValue, onSegmentChangeBase, clearPrefillMarkers],\n );\n\n const onInputChange = React.useCallback(\n (val: string) => {\n if (val !== prefilledValueRef.current) {\n prefilledFromRecentRef.current = false;\n }\n onInputChangeBase(val);\n },\n [onInputChangeBase],\n );\n\n const { canShowContinue, canSubmit } = getProceedEligibility({\n mode,\n currentValue,\n accountExists: runtime.accountExists,\n secure,\n });\n\n const [waiting, setWaiting] = React.useState(false);\n const [showScanDevice, setShowScanDevice] = React.useState(false);\n const [showEmailRecovery, setShowEmailRecovery] = React.useState(false);\n\n // If the user is attempting to register but we discover the account already exists,\n // automatically switch them to the Login tab.\n React.useEffect(() => {\n if (waiting) return;\n if (mode !== AuthMenuMode.Register) return;\n if (!runtime.accountExists) return;\n if (lastUserSelectedModeRef.current === AuthMenuMode.Register) return;\n setMode(AuthMenuMode.Login);\n }, [mode, runtime.accountExists, setMode, waiting]);\n\n // Lazy feature-island: entering Login can prefill the last used account username.\n React.useEffect(() => {\n const prevMode = prevModeRef.current;\n prevModeRef.current = mode;\n\n const enteringLogin = mode === AuthMenuMode.Login && prevMode !== AuthMenuMode.Login;\n if (!enteringLogin) return;\n if (latestValueRef.current.trim().length > 0) return;\n\n let cancelled = false;\n void import('../features/recentLoginPrefill')\n .then(async (m) => {\n const result = await m.getRecentLoginPrefill(runtime.tatchiPasskey);\n if (cancelled || !result?.username) return;\n if (prevModeRef.current !== AuthMenuMode.Login) return;\n if (latestValueRef.current.trim().length > 0) return;\n\n setCurrentValue(result.username);\n prefilledFromRecentRef.current = true;\n prefilledValueRef.current = result.username;\n })\n .catch(() => {});\n\n return () => {\n cancelled = true;\n };\n }, [mode, runtime.tatchiPasskey, setCurrentValue]);\n\n const fallbackOnEvent = React.useCallback((event: DeviceLinkingSSEEvent) => {\n console.log('ShowQRCode event:', event);\n }, []);\n\n const fallbackOnError = React.useCallback((error: Error) => {\n console.error('ShowQRCode error:', error);\n }, []);\n\n const handleLinkDeviceEvent = props.linkDeviceOptions?.onEvent ?? fallbackOnEvent;\n const handleLinkDeviceError = props.linkDeviceOptions?.onError ?? fallbackOnError;\n const handleLinkDeviceCancelled = props.linkDeviceOptions?.onCancelled;\n\n const stopLinkDeviceFlow = React.useCallback(() => {\n const stopper = runtime.stopDevice2LinkingFlow;\n if (!stopper) return;\n void stopper().catch(() => {});\n }, [runtime.stopDevice2LinkingFlow]);\n\n const closeLinkDeviceView = React.useCallback(\n (reason: 'user' | 'flow') => {\n stopLinkDeviceFlow();\n setShowScanDevice(false);\n if (reason === 'user') {\n handleLinkDeviceCancelled?.();\n }\n },\n [stopLinkDeviceFlow, handleLinkDeviceCancelled],\n );\n\n const onResetToStart = React.useCallback(() => {\n setWaiting(false);\n if (showScanDevice) {\n closeLinkDeviceView('user');\n } else {\n setShowScanDevice(false);\n }\n setShowEmailRecovery(false);\n lastUserSelectedModeRef.current = null;\n resetToDefault();\n setCurrentValue('');\n clearPrefillMarkers();\n }, [showScanDevice, closeLinkDeviceView, resetToDefault, setCurrentValue, clearPrefillMarkers]);\n\n const onProceed = React.useCallback(() => {\n if (!canSubmit) return;\n\n setWaiting(true);\n\n void (async () => {\n try {\n if (mode === AuthMenuMode.Sync) {\n await props.onSyncAccount?.();\n setWaiting(false);\n setMode(AuthMenuMode.Login);\n } else if (mode === AuthMenuMode.Login) {\n await props.onLogin?.();\n setWaiting(false);\n closeLinkDeviceView('flow');\n setMode(AuthMenuMode.Login);\n } else {\n await props.onRegister?.();\n setWaiting(false);\n setMode(AuthMenuMode.Login);\n }\n } catch {\n if (mode === AuthMenuMode.Login) {\n setWaiting(false);\n closeLinkDeviceView('flow');\n setMode(mode);\n return;\n }\n onResetToStart();\n }\n })();\n }, [\n canSubmit,\n mode,\n props.onSyncAccount,\n props.onLogin,\n props.onRegister,\n setMode,\n closeLinkDeviceView,\n onResetToStart,\n ]);\n\n const openScanDevice = React.useCallback(() => {\n setShowEmailRecovery(false);\n setShowScanDevice(true);\n }, []);\n\n const openEmailRecovery = React.useCallback(() => {\n stopLinkDeviceFlow();\n setShowScanDevice(false);\n setShowEmailRecovery(true);\n }, [stopLinkDeviceFlow]);\n\n const closeEmailRecovery = React.useCallback(() => {\n setShowEmailRecovery(false);\n }, []);\n\n const linkDevice: PasskeyAuthMenuLinkDeviceController = React.useMemo(\n () => ({\n isOpen: showScanDevice,\n onClose: () => closeLinkDeviceView('flow'),\n onEvent: handleLinkDeviceEvent,\n onError: handleLinkDeviceError,\n }),\n [showScanDevice, closeLinkDeviceView, handleLinkDeviceEvent, handleLinkDeviceError],\n );\n\n return {\n mode,\n title,\n waiting,\n showScanDevice,\n showEmailRecovery,\n currentValue,\n postfixText: runtime.displayPostfix,\n isUsingExistingAccount: runtime.isUsingExistingAccount,\n secure,\n canShowContinue,\n canSubmit,\n onSegmentChange,\n onInputChange,\n onProceed,\n onResetToStart,\n openScanDevice,\n openEmailRecovery,\n closeEmailRecovery,\n closeLinkDeviceView,\n linkDevice,\n };\n}\n\nexport default usePasskeyAuthMenuController;\n"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+
3
+ //#region src/react/components/PasskeyAuthMenu/hydrationContext.ts
4
+ /**
5
+ * Internal context used by the SSR-safe shell to hint the client controller about
6
+ * initial hydration strategy (e.g., align first render with the skeleton).
7
+ *
8
+ * Default: false (normal behavior).
9
+ */
10
+ const PasskeyAuthMenuHydrationContext = React.createContext(false);
11
+ function usePasskeyAuthMenuForceInitialRegister() {
12
+ return React.useContext(PasskeyAuthMenuHydrationContext);
13
+ }
14
+
15
+ //#endregion
16
+ export { PasskeyAuthMenuHydrationContext, usePasskeyAuthMenuForceInitialRegister };
17
+ //# sourceMappingURL=hydrationContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hydrationContext.js","names":[],"sources":["../../../../../src/react/components/PasskeyAuthMenu/hydrationContext.ts"],"sourcesContent":["import React from 'react';\n\n/**\n * Internal context used by the SSR-safe shell to hint the client controller about\n * initial hydration strategy (e.g., align first render with the skeleton).\n *\n * Default: false (normal behavior).\n */\nexport const PasskeyAuthMenuHydrationContext = React.createContext<boolean>(false);\n\nexport function usePasskeyAuthMenuForceInitialRegister(): boolean {\n return React.useContext(PasskeyAuthMenuHydrationContext);\n}\n\n"],"mappings":";;;;;;;;;AAQA,MAAa,kCAAkC,MAAM,cAAuB;AAE5E,SAAgB,yCAAkD;AAChE,QAAO,MAAM,WAAW"}
@@ -1,6 +1,6 @@
1
+ import { AuthMenuMode, AuthMenuModeMap } from "./authMenuTypes.js";
1
2
  import { PasskeyAuthMenuSkeleton } from "./skeleton.js";
2
3
  import { PasskeyAuthMenu } from "./shell.js";
3
- import { AuthMenuMode, AuthMenuModeMap } from "./authMenuTypes.js";
4
4
 
5
5
  //#region src/react/components/PasskeyAuthMenu/passkeyAuthMenuCompat.ts
6
6
  var passkeyAuthMenuCompat_default = PasskeyAuthMenu;
@@ -1,13 +1,33 @@
1
1
  import { useTheme } from "../theme/ThemeProvider.js";
2
+ import "./PasskeyAuthMenu.js";
2
3
  import { PasskeyAuthMenuThemeScope } from "./themeScope.js";
4
+ import { AuthMenuMode } from "./authMenuTypes.js";
3
5
  import { PasskeyAuthMenuSkeletonInner } from "./skeleton.js";
4
6
  import { preloadPasskeyAuthMenu } from "./preload.js";
7
+ import { PasskeyAuthMenuHydrationContext } from "./hydrationContext.js";
5
8
  import React from "react";
6
9
  import { jsx, jsxs } from "react/jsx-runtime";
7
10
 
8
11
  //#region src/react/components/PasskeyAuthMenu/shell.tsx
9
- function createClientLazy() {
10
- return React.lazy(() => import("./client.js").then((m) => ({ default: m.PasskeyAuthMenuClient })));
12
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
13
+ const clientLazyCache = /* @__PURE__ */ new Map();
14
+ let didClientMountOnce = false;
15
+ let didAutoPreloadClientChunk = false;
16
+ function autoPreloadClientChunk() {
17
+ if (didAutoPreloadClientChunk) return;
18
+ didAutoPreloadClientChunk = true;
19
+ preloadPasskeyAuthMenu();
20
+ }
21
+ if (typeof window !== "undefined" && typeof document !== "undefined") autoPreloadClientChunk();
22
+ function getClientLazy(retryKey) {
23
+ const existing = clientLazyCache.get(retryKey);
24
+ if (existing) return existing;
25
+ const next = React.lazy(() => import("./client.js").then((m) => ({ default: m.PasskeyAuthMenuClient })));
26
+ clientLazyCache.set(retryKey, next);
27
+ return next;
28
+ }
29
+ function invalidateClientLazy(retryKey) {
30
+ clientLazyCache.delete(retryKey);
11
31
  }
12
32
  var LazyErrorBoundary = class extends React.Component {
13
33
  state = { error: null };
@@ -18,6 +38,9 @@ var LazyErrorBoundary = class extends React.Component {
18
38
  this.setState({ error: null });
19
39
  this.props.onRetry();
20
40
  };
41
+ componentDidCatch(error) {
42
+ this.props.onError?.(error);
43
+ }
21
44
  render() {
22
45
  if (this.state.error) return this.props.fallback({
23
46
  error: this.state.error,
@@ -33,43 +56,54 @@ var LazyErrorBoundary = class extends React.Component {
33
56
  * - Client: lazy-loads the full implementation after mount.
34
57
  */
35
58
  const PasskeyAuthMenu = (props) => {
36
- const [isClient, setIsClient] = React.useState(false);
59
+ const [isClient, setIsClient] = React.useState(() => {
60
+ if (typeof window === "undefined") return false;
61
+ return didClientMountOnce;
62
+ });
63
+ const forceInitialRegisterRef = React.useRef(!didClientMountOnce && (props.defaultMode == null || props.defaultMode === AuthMenuMode.Register));
37
64
  const [retryKey, setRetryKey] = React.useState(0);
38
- const ClientLazy = React.useMemo(() => createClientLazy(), [retryKey]);
65
+ const ClientLazy = React.useMemo(() => getClientLazy(retryKey), [retryKey]);
39
66
  const { theme } = useTheme();
40
- React.useEffect(() => {
67
+ useIsomorphicLayoutEffect(() => {
68
+ didClientMountOnce = true;
41
69
  setIsClient(true);
42
- preloadPasskeyAuthMenu();
70
+ autoPreloadClientChunk();
43
71
  }, []);
44
72
  const skeleton = /* @__PURE__ */ jsx(PasskeyAuthMenuSkeletonInner, {
45
73
  className: props.className,
46
- style: props.style
74
+ style: props.style,
75
+ defaultMode: props.defaultMode,
76
+ headings: props.headings
47
77
  });
48
78
  return /* @__PURE__ */ jsx(PasskeyAuthMenuThemeScope, {
49
79
  theme,
50
- children: isClient ? /* @__PURE__ */ jsx(LazyErrorBoundary, {
51
- onRetry: () => setRetryKey((k) => k + 1),
52
- fallback: ({ retry }) => /* @__PURE__ */ jsxs("div", { children: [skeleton, /* @__PURE__ */ jsxs("div", {
53
- style: {
54
- marginTop: 10,
55
- fontSize: 12,
56
- textAlign: "center",
57
- opacity: .9
58
- },
59
- children: [
60
- "Failed to load menu.",
61
- " ",
62
- /* @__PURE__ */ jsx("button", {
63
- type: "button",
64
- onClick: retry,
65
- style: { textDecoration: "underline" },
66
- children: "Retry"
67
- })
68
- ]
69
- })] }),
70
- children: /* @__PURE__ */ jsx(React.Suspense, {
71
- fallback: skeleton,
72
- children: /* @__PURE__ */ jsx(ClientLazy, { ...props })
80
+ children: isClient ? /* @__PURE__ */ jsx(PasskeyAuthMenuHydrationContext.Provider, {
81
+ value: forceInitialRegisterRef.current,
82
+ children: /* @__PURE__ */ jsx(LazyErrorBoundary, {
83
+ onRetry: () => setRetryKey((k) => k + 1),
84
+ onError: () => invalidateClientLazy(retryKey),
85
+ fallback: ({ retry }) => /* @__PURE__ */ jsxs("div", { children: [skeleton, /* @__PURE__ */ jsxs("div", {
86
+ style: {
87
+ marginTop: 10,
88
+ fontSize: 12,
89
+ textAlign: "center",
90
+ opacity: .9
91
+ },
92
+ children: [
93
+ "Failed to load menu.",
94
+ " ",
95
+ /* @__PURE__ */ jsx("button", {
96
+ type: "button",
97
+ onClick: retry,
98
+ style: { textDecoration: "underline" },
99
+ children: "Retry"
100
+ })
101
+ ]
102
+ })] }),
103
+ children: /* @__PURE__ */ jsx(React.Suspense, {
104
+ fallback: skeleton,
105
+ children: /* @__PURE__ */ jsx(ClientLazy, { ...props })
106
+ })
73
107
  })
74
108
  }) : skeleton
75
109
  });
@@ -1 +1 @@
1
- {"version":3,"file":"shell.js","names":["PasskeyAuthMenu: React.FC<PasskeyAuthMenuProps>"],"sources":["../../../../../src/react/components/PasskeyAuthMenu/shell.tsx"],"sourcesContent":["import React from 'react';\nimport { PasskeyAuthMenuSkeletonInner } from './skeleton';\nimport { PasskeyAuthMenuThemeScope } from './themeScope';\nimport type { PasskeyAuthMenuProps } from './types';\nimport { useTheme } from '../theme';\nimport { preloadPasskeyAuthMenu } from './preload';\n\nfunction createClientLazy() {\n return React.lazy(() => import('./client').then((m) => ({ default: m.PasskeyAuthMenuClient })));\n}\n\nclass LazyErrorBoundary extends React.Component<\n {\n fallback: (args: { error: Error; retry: () => void }) => React.ReactNode;\n onRetry: () => void;\n children: React.ReactNode;\n },\n { error: Error | null }\n> {\n state: { error: Error | null } = { error: null };\n\n static getDerivedStateFromError(error: Error): { error: Error } {\n return { error };\n }\n\n retry = () => {\n this.setState({ error: null });\n this.props.onRetry();\n };\n\n render() {\n if (this.state.error) {\n return this.props.fallback({ error: this.state.error, retry: this.retry });\n }\n return this.props.children;\n }\n}\n\n/**\n * `PasskeyAuthMenu` — SSR-safe shell.\n *\n * - Server: renders a skeleton only.\n * - Client: lazy-loads the full implementation after mount.\n */\nexport const PasskeyAuthMenu: React.FC<PasskeyAuthMenuProps> = (props) => {\n const [isClient, setIsClient] = React.useState(false);\n const [retryKey, setRetryKey] = React.useState(0);\n const ClientLazy = React.useMemo(() => createClientLazy(), [retryKey]);\n\n // Align with the SDK Theme boundary when present (TatchiPasskeyProvider wraps one by default).\n // Falls back to system preference when used standalone.\n const { theme } = useTheme();\n\n React.useEffect(() => {\n setIsClient(true);\n // Start fetching the client chunk immediately; the skeleton remains as the Suspense fallback.\n preloadPasskeyAuthMenu();\n }, []);\n\n const skeleton = (\n <PasskeyAuthMenuSkeletonInner className={props.className} style={props.style} />\n );\n\n return (\n <PasskeyAuthMenuThemeScope theme={theme}>\n {isClient ? (\n <LazyErrorBoundary\n onRetry={() => setRetryKey((k) => k + 1)}\n fallback={({ retry }) => (\n <div>\n {skeleton}\n <div style={{ marginTop: 10, fontSize: 12, textAlign: 'center', opacity: 0.9 }}>\n Failed to load menu.{' '}\n <button type=\"button\" onClick={retry} style={{ textDecoration: 'underline' }}>\n Retry\n </button>\n </div>\n </div>\n )}\n >\n <React.Suspense\n fallback={skeleton}\n >\n <ClientLazy {...props} />\n </React.Suspense>\n </LazyErrorBoundary>\n ) : (\n skeleton\n )}\n </PasskeyAuthMenuThemeScope>\n );\n};\n\nexport default PasskeyAuthMenu;\n"],"mappings":";;;;;;;;AAOA,SAAS,mBAAmB;AAC1B,QAAO,MAAM,WAAW,OAAO,eAAY,MAAM,OAAO,EAAE,SAAS,EAAE;;AAGvE,IAAM,oBAAN,cAAgC,MAAM,UAOpC;CACA,QAAiC,EAAE,OAAO;CAE1C,OAAO,yBAAyB,OAAgC;AAC9D,SAAO,EAAE;;CAGX,cAAc;AACZ,OAAK,SAAS,EAAE,OAAO;AACvB,OAAK,MAAM;;CAGb,SAAS;AACP,MAAI,KAAK,MAAM,MACb,QAAO,KAAK,MAAM,SAAS;GAAE,OAAO,KAAK,MAAM;GAAO,OAAO,KAAK;;AAEpE,SAAO,KAAK,MAAM;;;;;;;;;AAUtB,MAAaA,mBAAmD,UAAU;CACxE,MAAM,CAAC,UAAU,eAAe,MAAM,SAAS;CAC/C,MAAM,CAAC,UAAU,eAAe,MAAM,SAAS;CAC/C,MAAM,aAAa,MAAM,cAAc,oBAAoB,CAAC;CAI5D,MAAM,EAAE,UAAU;AAElB,OAAM,gBAAgB;AACpB,cAAY;AAEZ;IACC;CAEH,MAAM,WACJ,oBAAC;EAA6B,WAAW,MAAM;EAAW,OAAO,MAAM;;AAGzE,QACE,oBAAC;EAAiC;YAC/B,WACC,oBAAC;GACC,eAAe,aAAa,MAAM,IAAI;GACtC,WAAW,EAAE,YACX,qBAAC,oBACE,UACD,qBAAC;IAAI,OAAO;KAAE,WAAW;KAAI,UAAU;KAAI,WAAW;KAAU,SAAS;;;KAAO;KACzD;KACrB,oBAAC;MAAO,MAAK;MAAS,SAAS;MAAO,OAAO,EAAE,gBAAgB;gBAAe;;;;aAOpF,oBAAC,MAAM;IACL,UAAU;cAEV,oBAAC,cAAW,GAAI;;OAIpB;;;AAMR,oBAAe"}
1
+ {"version":3,"file":"shell.js","names":["PasskeyAuthMenu: React.FC<PasskeyAuthMenuProps>"],"sources":["../../../../../src/react/components/PasskeyAuthMenu/shell.tsx"],"sourcesContent":["import React from 'react';\nimport './PasskeyAuthMenu.css';\nimport { PasskeyAuthMenuSkeletonInner } from './skeleton';\nimport { PasskeyAuthMenuThemeScope } from './themeScope';\nimport { AuthMenuMode, type PasskeyAuthMenuProps } from './types';\nimport { useTheme } from '../theme';\nimport { preloadPasskeyAuthMenu } from './preload';\nimport { PasskeyAuthMenuHydrationContext } from './hydrationContext';\n\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;\n\ntype PasskeyAuthMenuClientComponent = React.ComponentType<PasskeyAuthMenuProps>;\n\nconst clientLazyCache = new Map<number, React.LazyExoticComponent<PasskeyAuthMenuClientComponent>>();\n\nlet didClientMountOnce = false;\n\nlet didAutoPreloadClientChunk = false;\nfunction autoPreloadClientChunk() {\n if (didAutoPreloadClientChunk) return;\n didAutoPreloadClientChunk = true;\n void preloadPasskeyAuthMenu();\n}\n\n// If this module is imported in a browser bundle, start fetching the client chunk immediately.\n// This reduces the chance of a first-mount Suspense fallback flash without affecting SSR.\nif (typeof window !== 'undefined' && typeof document !== 'undefined') {\n autoPreloadClientChunk();\n}\n\nfunction getClientLazy(retryKey: number): React.LazyExoticComponent<PasskeyAuthMenuClientComponent> {\n const existing = clientLazyCache.get(retryKey);\n if (existing) return existing;\n\n const next = React.lazy(() =>\n import('./client').then((m) => ({ default: m.PasskeyAuthMenuClient })),\n ) as unknown as React.LazyExoticComponent<PasskeyAuthMenuClientComponent>;\n\n clientLazyCache.set(retryKey, next);\n return next;\n}\n\nfunction invalidateClientLazy(retryKey: number) {\n clientLazyCache.delete(retryKey);\n}\n\nclass LazyErrorBoundary extends React.Component<\n {\n fallback: (args: { error: Error; retry: () => void }) => React.ReactNode;\n onRetry: () => void;\n onError?: (error: Error) => void;\n children: React.ReactNode;\n },\n { error: Error | null }\n> {\n state: { error: Error | null } = { error: null };\n\n static getDerivedStateFromError(error: Error): { error: Error } {\n return { error };\n }\n\n retry = () => {\n this.setState({ error: null });\n this.props.onRetry();\n };\n\n componentDidCatch(error: Error) {\n this.props.onError?.(error);\n }\n\n render() {\n if (this.state.error) {\n return this.props.fallback({ error: this.state.error, retry: this.retry });\n }\n return this.props.children;\n }\n}\n\n/**\n * `PasskeyAuthMenu` — SSR-safe shell.\n *\n * - Server: renders a skeleton only.\n * - Client: lazy-loads the full implementation after mount.\n */\nexport const PasskeyAuthMenu: React.FC<PasskeyAuthMenuProps> = (props) => {\n const [isClient, setIsClient] = React.useState(() => {\n if (typeof window === 'undefined') return false;\n return didClientMountOnce;\n });\n const forceInitialRegisterRef = React.useRef(\n !didClientMountOnce && (props.defaultMode == null || props.defaultMode === AuthMenuMode.Register),\n );\n const [retryKey, setRetryKey] = React.useState(0);\n const ClientLazy = React.useMemo(() => getClientLazy(retryKey), [retryKey]);\n\n // Align with the SDK Theme boundary when present (TatchiPasskeyProvider wraps one by default).\n // Falls back to system preference when used standalone.\n const { theme } = useTheme();\n\n useIsomorphicLayoutEffect(() => {\n didClientMountOnce = true;\n setIsClient(true);\n // Start fetching the client chunk immediately; the skeleton remains as the Suspense fallback.\n autoPreloadClientChunk();\n }, []);\n\n const skeleton = (\n <PasskeyAuthMenuSkeletonInner\n className={props.className}\n style={props.style}\n defaultMode={props.defaultMode}\n headings={props.headings}\n />\n );\n\n return (\n <PasskeyAuthMenuThemeScope theme={theme}>\n {isClient ? (\n <PasskeyAuthMenuHydrationContext.Provider value={forceInitialRegisterRef.current}>\n <LazyErrorBoundary\n onRetry={() => setRetryKey((k) => k + 1)}\n onError={() => invalidateClientLazy(retryKey)}\n fallback={({ retry }) => (\n <div>\n {skeleton}\n <div style={{ marginTop: 10, fontSize: 12, textAlign: 'center', opacity: 0.9 }}>\n Failed to load menu.{' '}\n <button type=\"button\" onClick={retry} style={{ textDecoration: 'underline' }}>\n Retry\n </button>\n </div>\n </div>\n )}\n >\n <React.Suspense fallback={skeleton}>\n <ClientLazy {...props} />\n </React.Suspense>\n </LazyErrorBoundary>\n </PasskeyAuthMenuHydrationContext.Provider>\n ) : (\n skeleton\n )}\n </PasskeyAuthMenuThemeScope>\n );\n};\n\nexport default PasskeyAuthMenu;\n"],"mappings":";;;;;;;;;;;AASA,MAAM,4BAA4B,OAAO,WAAW,cAAc,MAAM,kBAAkB,MAAM;AAIhG,MAAM,kCAAkB,IAAI;AAE5B,IAAI,qBAAqB;AAEzB,IAAI,4BAA4B;AAChC,SAAS,yBAAyB;AAChC,KAAI,0BAA2B;AAC/B,6BAA4B;AAC5B,CAAK;;AAKP,IAAI,OAAO,WAAW,eAAe,OAAO,aAAa,YACvD;AAGF,SAAS,cAAc,UAA6E;CAClG,MAAM,WAAW,gBAAgB,IAAI;AACrC,KAAI,SAAU,QAAO;CAErB,MAAM,OAAO,MAAM,WACjB,OAAO,eAAY,MAAM,OAAO,EAAE,SAAS,EAAE;AAG/C,iBAAgB,IAAI,UAAU;AAC9B,QAAO;;AAGT,SAAS,qBAAqB,UAAkB;AAC9C,iBAAgB,OAAO;;AAGzB,IAAM,oBAAN,cAAgC,MAAM,UAQpC;CACA,QAAiC,EAAE,OAAO;CAE1C,OAAO,yBAAyB,OAAgC;AAC9D,SAAO,EAAE;;CAGX,cAAc;AACZ,OAAK,SAAS,EAAE,OAAO;AACvB,OAAK,MAAM;;CAGb,kBAAkB,OAAc;AAC9B,OAAK,MAAM,UAAU;;CAGvB,SAAS;AACP,MAAI,KAAK,MAAM,MACb,QAAO,KAAK,MAAM,SAAS;GAAE,OAAO,KAAK,MAAM;GAAO,OAAO,KAAK;;AAEpE,SAAO,KAAK,MAAM;;;;;;;;;AAUtB,MAAaA,mBAAmD,UAAU;CACxE,MAAM,CAAC,UAAU,eAAe,MAAM,eAAe;AACnD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO;;CAET,MAAM,0BAA0B,MAAM,OACpC,CAAC,uBAAuB,MAAM,eAAe,QAAQ,MAAM,gBAAgB,aAAa;CAE1F,MAAM,CAAC,UAAU,eAAe,MAAM,SAAS;CAC/C,MAAM,aAAa,MAAM,cAAc,cAAc,WAAW,CAAC;CAIjE,MAAM,EAAE,UAAU;AAElB,iCAAgC;AAC9B,uBAAqB;AACrB,cAAY;AAEZ;IACC;CAEH,MAAM,WACJ,oBAAC;EACC,WAAW,MAAM;EACjB,OAAO,MAAM;EACb,aAAa,MAAM;EACnB,UAAU,MAAM;;AAIpB,QACE,oBAAC;EAAiC;YAC/B,WACC,oBAAC,gCAAgC;GAAS,OAAO,wBAAwB;aACvE,oBAAC;IACC,eAAe,aAAa,MAAM,IAAI;IACtC,eAAe,qBAAqB;IACpC,WAAW,EAAE,YACX,qBAAC,oBACE,UACD,qBAAC;KAAI,OAAO;MAAE,WAAW;MAAI,UAAU;MAAI,WAAW;MAAU,SAAS;;;MAAO;MACzD;MACrB,oBAAC;OAAO,MAAK;OAAS,SAAS;OAAO,OAAO,EAAE,gBAAgB;iBAAe;;;;cAOpF,oBAAC,MAAM;KAAS,UAAU;eACxB,oBAAC,cAAW,GAAI;;;OAKtB;;;AAMR,oBAAe"}