@swype-org/react-sdk 0.1.84 → 0.1.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -167,6 +167,7 @@ __export(api_exports, {
167
167
  fetchUserConfig: () => fetchUserConfig,
168
168
  registerPasskey: () => registerPasskey,
169
169
  reportActionCompletion: () => reportActionCompletion,
170
+ reportPasskeyActivity: () => reportPasskeyActivity,
170
171
  signTransfer: () => signTransfer,
171
172
  updateUserConfig: () => updateUserConfig,
172
173
  updateUserConfigBySession: () => updateUserConfigBySession
@@ -289,6 +290,17 @@ async function registerPasskey(apiBaseUrl, token, credentialId, publicKey) {
289
290
  });
290
291
  if (!res.ok) await throwApiError(res);
291
292
  }
293
+ async function reportPasskeyActivity(apiBaseUrl, token, credentialId) {
294
+ const res = await fetch(`${apiBaseUrl}/v1/users/config/passkey`, {
295
+ method: "PATCH",
296
+ headers: {
297
+ "Content-Type": "application/json",
298
+ Authorization: `Bearer ${token}`
299
+ },
300
+ body: JSON.stringify({ credentialId })
301
+ });
302
+ if (!res.ok) await throwApiError(res);
303
+ }
292
304
  async function fetchUserConfig(apiBaseUrl, token) {
293
305
  const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
294
306
  headers: { Authorization: `Bearer ${token}` }
@@ -788,6 +800,66 @@ function createPasskeyViaPopup(options, existingCredentialIds = []) {
788
800
  }
789
801
  });
790
802
  }
803
+ var VERIFY_POPUP_TIMEOUT_MS = 6e4;
804
+ function findDevicePasskeyViaPopup(options) {
805
+ return new Promise((resolve, reject) => {
806
+ const channelId = `swype-pv-${Date.now()}-${Math.random().toString(36).slice(2)}`;
807
+ const payload = { ...options, channelId };
808
+ const encoded = btoa(JSON.stringify(payload));
809
+ const popupUrl = `${window.location.origin}/passkey-verify#${encoded}`;
810
+ const popup = window.open(popupUrl, "swype-passkey-verify");
811
+ if (!popup) {
812
+ reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
813
+ return;
814
+ }
815
+ let settled = false;
816
+ const channel = typeof BroadcastChannel !== "undefined" ? new BroadcastChannel(channelId) : null;
817
+ const timer = setTimeout(() => {
818
+ cleanup();
819
+ resolve(null);
820
+ }, VERIFY_POPUP_TIMEOUT_MS);
821
+ const closedPoll = setInterval(() => {
822
+ if (popup.closed && !settled) {
823
+ clearInterval(closedPoll);
824
+ setTimeout(() => {
825
+ if (!settled) {
826
+ cleanup();
827
+ resolve(null);
828
+ }
829
+ }, POPUP_CLOSED_GRACE_MS);
830
+ }
831
+ }, POPUP_CLOSED_POLL_MS);
832
+ function handleResult(data) {
833
+ if (settled) return;
834
+ if (!data || typeof data !== "object") return;
835
+ if (data.type !== "swype:passkey-verify-result") return;
836
+ settled = true;
837
+ cleanup();
838
+ if (data.error) {
839
+ resolve(null);
840
+ } else if (data.result && typeof data.result === "object") {
841
+ const result = data.result;
842
+ resolve(result.credentialId ?? null);
843
+ } else {
844
+ resolve(null);
845
+ }
846
+ }
847
+ if (channel) {
848
+ channel.onmessage = (event) => handleResult(event.data);
849
+ }
850
+ const postMessageHandler = (event) => {
851
+ if (event.source !== popup) return;
852
+ handleResult(event.data);
853
+ };
854
+ window.addEventListener("message", postMessageHandler);
855
+ function cleanup() {
856
+ clearTimeout(timer);
857
+ clearInterval(closedPoll);
858
+ window.removeEventListener("message", postMessageHandler);
859
+ channel?.close();
860
+ }
861
+ });
862
+ }
791
863
  async function checkServerForNewPasskey(authToken, apiBaseUrl, existingCredentialIds) {
792
864
  if (!authToken || !apiBaseUrl) return null;
793
865
  const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
@@ -2773,6 +2845,72 @@ var errorBannerStyle2 = (tokens) => ({
2773
2845
  width: "100%",
2774
2846
  textAlign: "left"
2775
2847
  });
2848
+ function VerifyPasskeyScreen({
2849
+ onVerify,
2850
+ onBack,
2851
+ verifying,
2852
+ error
2853
+ }) {
2854
+ const { tokens } = useSwypeConfig();
2855
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2856
+ ScreenLayout,
2857
+ {
2858
+ footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2859
+ /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onVerify, disabled: verifying, loading: verifying, children: "Verify passkey" }),
2860
+ /* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
2861
+ ] }),
2862
+ children: [
2863
+ /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onBack }),
2864
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle4, children: [
2865
+ /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "accent", size: 64, children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "36", height: "36", viewBox: "0 0 24 24", fill: "none", children: [
2866
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "4", y: "4", width: "16", height: "16", rx: "3", stroke: tokens.accent, strokeWidth: "1.5", strokeDasharray: "3 2" }),
2867
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "10", r: "1", fill: tokens.accent }),
2868
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "15", cy: "10", r: "1", fill: tokens.accent }),
2869
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 14c0 1.5 1.34 2.5 3 2.5s3-1 3-2.5", stroke: tokens.accent, strokeWidth: "1.2", strokeLinecap: "round" })
2870
+ ] }) }),
2871
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle4(tokens.text), children: "Verify your passkey" }),
2872
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle4(tokens.textSecondary), children: "Your browser requires a separate window to verify your passkey. Tap the button below to continue." }),
2873
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle3(tokens), children: error }),
2874
+ /* @__PURE__ */ jsxRuntime.jsx(InfoBanner, { children: "Your passkey is stored securely on your device. Swype never sees your biometric data." })
2875
+ ] })
2876
+ ]
2877
+ }
2878
+ );
2879
+ }
2880
+ var contentStyle4 = {
2881
+ textAlign: "center",
2882
+ flex: 1,
2883
+ display: "flex",
2884
+ flexDirection: "column",
2885
+ alignItems: "center",
2886
+ paddingTop: 32
2887
+ };
2888
+ var headingStyle4 = (color) => ({
2889
+ fontSize: "1.45rem",
2890
+ fontWeight: 700,
2891
+ letterSpacing: "-0.02em",
2892
+ color,
2893
+ margin: "24px 0 8px"
2894
+ });
2895
+ var subtitleStyle4 = (color) => ({
2896
+ fontSize: "0.9rem",
2897
+ color,
2898
+ margin: "0 0 28px",
2899
+ lineHeight: 1.5,
2900
+ maxWidth: 280
2901
+ });
2902
+ var errorBannerStyle3 = (tokens) => ({
2903
+ background: tokens.errorBg,
2904
+ border: `1px solid ${tokens.error}66`,
2905
+ borderRadius: 16,
2906
+ padding: "11px 14px",
2907
+ color: tokens.error,
2908
+ fontSize: "0.84rem",
2909
+ marginBottom: 14,
2910
+ lineHeight: 1.5,
2911
+ width: "100%",
2912
+ textAlign: "left"
2913
+ });
2776
2914
  var WALLET_EMOJIS = {
2777
2915
  rabby: "\u{1F430}",
2778
2916
  ora: "\u2666\uFE0F",
@@ -2815,8 +2953,8 @@ function WalletPickerScreen({
2815
2953
  children: [
2816
2954
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { title: "Set up Swype", onBack }),
2817
2955
  hasPending && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2818
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle4(tokens.text), children: "Continue where you left off" }),
2819
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle4(tokens.textSecondary), children: "You have a wallet that still needs setup" }),
2956
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle5(tokens.text), children: "Continue where you left off" }),
2957
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle5(tokens.textSecondary), children: "You have a wallet that still needs setup" }),
2820
2958
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: pendingListStyle, children: pendingConnections.map((acct) => {
2821
2959
  const wallet = acct.wallets[0];
2822
2960
  const address = wallet ? truncateAddress(wallet.name) : void 0;
@@ -2856,8 +2994,8 @@ function WalletPickerScreen({
2856
2994
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: dividerStyle2(tokens.border), children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: dividerTextStyle(tokens.textMuted), children: "Or connect a new wallet" }) })
2857
2995
  ] }),
2858
2996
  !hasPending && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2859
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle4(tokens.text), children: "Where are your stablecoins?" }),
2860
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle4(tokens.textSecondary), children: "Select the wallet you want to deposit from" })
2997
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle5(tokens.text), children: "Where are your stablecoins?" }),
2998
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle5(tokens.textSecondary), children: "Select the wallet you want to deposit from" })
2861
2999
  ] }),
2862
3000
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: gridStyle, children: [
2863
3001
  displayProviders.map((p) => {
@@ -2910,14 +3048,14 @@ function WalletPickerScreen({
2910
3048
  }
2911
3049
  );
2912
3050
  }
2913
- var headingStyle4 = (color) => ({
3051
+ var headingStyle5 = (color) => ({
2914
3052
  fontSize: "1.35rem",
2915
3053
  fontWeight: 700,
2916
3054
  letterSpacing: "-0.02em",
2917
3055
  color,
2918
3056
  margin: "8px 0 4px"
2919
3057
  });
2920
- var subtitleStyle4 = (color) => ({
3058
+ var subtitleStyle5 = (color) => ({
2921
3059
  fontSize: "0.88rem",
2922
3060
  color,
2923
3061
  margin: "0 0 24px"
@@ -3093,9 +3231,9 @@ function SetupScreen({
3093
3231
  ] }),
3094
3232
  children: [
3095
3233
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { title: "Swype Setup", onBack, right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
3096
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle5(tokens.text), children: "Set up One-Tap deposits" }),
3097
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle5(tokens.textSecondary), children: "Set your limit for instant deposits. Like a contactless card \u2014 you choose the max." }),
3098
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle3(tokens), children: error }),
3234
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle6(tokens.text), children: "Set up One-Tap deposits" }),
3235
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle6(tokens.textSecondary), children: "Set your limit for instant deposits. Like a contactless card \u2014 you choose the max." }),
3236
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle4(tokens), children: error }),
3099
3237
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: balanceRowStyle, children: [
3100
3238
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: balanceLeftStyle, children: [
3101
3239
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: coinIconStyle(tokens.accent), children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [
@@ -3173,20 +3311,20 @@ function SetupScreen({
3173
3311
  }
3174
3312
  );
3175
3313
  }
3176
- var headingStyle5 = (color) => ({
3314
+ var headingStyle6 = (color) => ({
3177
3315
  fontSize: "1.3rem",
3178
3316
  fontWeight: 700,
3179
3317
  letterSpacing: "-0.02em",
3180
3318
  color,
3181
3319
  margin: "8px 0 4px"
3182
3320
  });
3183
- var subtitleStyle5 = (color) => ({
3321
+ var subtitleStyle6 = (color) => ({
3184
3322
  fontSize: "0.86rem",
3185
3323
  color,
3186
3324
  margin: "0 0 24px",
3187
3325
  lineHeight: 1.5
3188
3326
  });
3189
- var errorBannerStyle3 = (tokens) => ({
3327
+ var errorBannerStyle4 = (tokens) => ({
3190
3328
  background: tokens.errorBg,
3191
3329
  border: `1px solid ${tokens.error}66`,
3192
3330
  borderRadius: 16,
@@ -3454,7 +3592,7 @@ function DepositScreen({
3454
3592
  "%)"
3455
3593
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: detailRowStyle(tokens.textMuted), children: "Fees calculated at time of transfer" })
3456
3594
  ] }),
3457
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle4(tokens), children: error })
3595
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle5(tokens), children: error })
3458
3596
  ]
3459
3597
  }
3460
3598
  );
@@ -3522,7 +3660,7 @@ var detailRowStyle = (color) => ({
3522
3660
  color,
3523
3661
  marginBottom: 4
3524
3662
  });
3525
- var errorBannerStyle4 = (tokens) => ({
3663
+ var errorBannerStyle5 = (tokens) => ({
3526
3664
  background: tokens.errorBg,
3527
3665
  border: `1px solid ${tokens.error}66`,
3528
3666
  borderRadius: 16,
@@ -3621,22 +3759,22 @@ function SuccessScreen({
3621
3759
  right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout })
3622
3760
  }
3623
3761
  ),
3624
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle4, children: [
3762
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle5, children: [
3625
3763
  succeeded ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3626
3764
  /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "success", size: 64, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z", fill: tokens.success }) }) }),
3627
- /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle6(tokens.text), children: [
3765
+ /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle7(tokens.text), children: [
3628
3766
  "$",
3629
3767
  amount.toFixed(2),
3630
3768
  " deposited"
3631
3769
  ] }),
3632
- merchantName && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle6(tokens.textSecondary), children: [
3770
+ merchantName && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle7(tokens.textSecondary), children: [
3633
3771
  "to ",
3634
3772
  merchantName
3635
3773
  ] })
3636
3774
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3637
3775
  /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "error", size: 64, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z", fill: tokens.error }) }) }),
3638
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle6(tokens.text), children: "Transfer failed" }),
3639
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle6(tokens.error), children: error })
3776
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle7(tokens.text), children: "Transfer failed" }),
3777
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle7(tokens.error), children: error })
3640
3778
  ] }),
3641
3779
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryCardStyle(tokens), children: [
3642
3780
  sourceName && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryRowStyle, children: [
@@ -3668,21 +3806,21 @@ function SuccessScreen({
3668
3806
  }
3669
3807
  );
3670
3808
  }
3671
- var contentStyle4 = {
3809
+ var contentStyle5 = {
3672
3810
  flex: 1,
3673
3811
  display: "flex",
3674
3812
  flexDirection: "column",
3675
3813
  alignItems: "center",
3676
3814
  paddingTop: 16
3677
3815
  };
3678
- var headingStyle6 = (color) => ({
3816
+ var headingStyle7 = (color) => ({
3679
3817
  fontSize: "1.5rem",
3680
3818
  fontWeight: 700,
3681
3819
  letterSpacing: "-0.02em",
3682
3820
  color,
3683
3821
  margin: "20px 0 4px"
3684
3822
  });
3685
- var subtitleStyle6 = (color) => ({
3823
+ var subtitleStyle7 = (color) => ({
3686
3824
  fontSize: "0.9rem",
3687
3825
  color,
3688
3826
  margin: "0 0 20px"
@@ -3788,7 +3926,7 @@ function SelectSourceScreen({
3788
3926
  right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout })
3789
3927
  }
3790
3928
  ),
3791
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle7(tokens.textMuted), children: "Choose which chain and token to pay from." }),
3929
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle8(tokens.textMuted), children: "Choose which chain and token to pay from." }),
3792
3930
  /* @__PURE__ */ jsxRuntime.jsx("label", { style: labelStyle2(tokens.textSecondary), children: "Chain" }),
3793
3931
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: optionListStyle, children: choices.map((chain) => {
3794
3932
  const isSelected = chain.chainName === selectedChainName;
@@ -3845,7 +3983,7 @@ function SelectSourceScreen({
3845
3983
  }
3846
3984
  );
3847
3985
  }
3848
- var subtitleStyle7 = (color) => ({
3986
+ var subtitleStyle8 = (color) => ({
3849
3987
  fontSize: "0.85rem",
3850
3988
  color,
3851
3989
  margin: "0 0 20px",
@@ -3960,8 +4098,8 @@ function AdvancedSourceScreen({
3960
4098
  right: /* @__PURE__ */ jsxRuntime.jsx("span", { style: advancedBadgeStyle(tokens.accent), children: "Advanced" })
3961
4099
  }
3962
4100
  ),
3963
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle7(tokens.text), children: "Set up One-Tap deposits" }),
3964
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle8(tokens.textSecondary), children: "Select a token source for your One-Tap deposits." }),
4101
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle8(tokens.text), children: "Set up One-Tap deposits" }),
4102
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle9(tokens.textSecondary), children: "Select a token source for your One-Tap deposits." }),
3965
4103
  /* @__PURE__ */ jsxRuntime.jsx("label", { style: labelStyle3(tokens.textSecondary), children: "Select tokens to approve" }),
3966
4104
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: chainListStyle, children: choices.map((chain) => {
3967
4105
  const isExpanded = expandedChain === chain.chainName;
@@ -4024,14 +4162,14 @@ var advancedBadgeStyle = (color) => ({
4024
4162
  padding: "3px 10px",
4025
4163
  letterSpacing: "0.02em"
4026
4164
  });
4027
- var headingStyle7 = (color) => ({
4165
+ var headingStyle8 = (color) => ({
4028
4166
  fontSize: "1.3rem",
4029
4167
  fontWeight: 700,
4030
4168
  letterSpacing: "-0.02em",
4031
4169
  color,
4032
4170
  margin: "8px 0 4px"
4033
4171
  });
4034
- var subtitleStyle8 = (color) => ({
4172
+ var subtitleStyle9 = (color) => ({
4035
4173
  fontSize: "0.86rem",
4036
4174
  color,
4037
4175
  margin: "0 0 20px",
@@ -4159,16 +4297,16 @@ function TransferStatusScreen({
4159
4297
  const steps = buildSteps(phase);
4160
4298
  return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { children: [
4161
4299
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
4162
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle5, children: [
4300
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle6, children: [
4163
4301
  /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
4164
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle8(tokens.text), children: "Processing Transfer..." }),
4165
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle5(tokens), children: error }),
4302
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle9(tokens.text), children: "Processing Transfer..." }),
4303
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle6(tokens), children: error }),
4166
4304
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: stepsWrapStyle, children: /* @__PURE__ */ jsxRuntime.jsx(StepList, { steps }) }),
4167
4305
  /* @__PURE__ */ jsxRuntime.jsx("p", { style: waitHintStyle(tokens.textMuted), children: "Usually takes a few seconds" })
4168
4306
  ] })
4169
4307
  ] });
4170
4308
  }
4171
- var contentStyle5 = {
4309
+ var contentStyle6 = {
4172
4310
  flex: 1,
4173
4311
  display: "flex",
4174
4312
  flexDirection: "column",
@@ -4177,14 +4315,14 @@ var contentStyle5 = {
4177
4315
  textAlign: "center",
4178
4316
  padding: "0 24px"
4179
4317
  };
4180
- var headingStyle8 = (color) => ({
4318
+ var headingStyle9 = (color) => ({
4181
4319
  fontSize: "1.45rem",
4182
4320
  fontWeight: 700,
4183
4321
  letterSpacing: "-0.02em",
4184
4322
  color,
4185
4323
  margin: "20px 0 16px"
4186
4324
  });
4187
- var errorBannerStyle5 = (tokens) => ({
4325
+ var errorBannerStyle6 = (tokens) => ({
4188
4326
  background: tokens.errorBg,
4189
4327
  border: `1px solid ${tokens.error}66`,
4190
4328
  borderRadius: 16,
@@ -4244,10 +4382,10 @@ function OpenWalletScreen({
4244
4382
  ] }),
4245
4383
  children: [
4246
4384
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
4247
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle6, children: [
4385
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle7, children: [
4248
4386
  logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: displayName, style: logoStyle }) : /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
4249
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle9(tokens.text), children: loading ? "Connecting..." : `Open ${displayName}` }),
4250
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle9(tokens.textSecondary), children: loading ? "Creating transfer and preparing your wallet link..." : `Continue in ${displayName} to authorize this connection.` }),
4387
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle10(tokens.text), children: loading ? "Connecting..." : `Open ${displayName}` }),
4388
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle10(tokens.textSecondary), children: loading ? "Creating transfer and preparing your wallet link..." : `Continue in ${displayName} to authorize this connection.` }),
4251
4389
  !loading && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: waitingBadgeStyle(tokens), children: [
4252
4390
  /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 14 }),
4253
4391
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Waiting for authorization..." })
@@ -4257,7 +4395,7 @@ function OpenWalletScreen({
4257
4395
  }
4258
4396
  );
4259
4397
  }
4260
- var contentStyle6 = {
4398
+ var contentStyle7 = {
4261
4399
  flex: 1,
4262
4400
  display: "flex",
4263
4401
  flexDirection: "column",
@@ -4277,14 +4415,14 @@ var logoStyle = {
4277
4415
  borderRadius: 14,
4278
4416
  objectFit: "contain"
4279
4417
  };
4280
- var headingStyle9 = (color) => ({
4418
+ var headingStyle10 = (color) => ({
4281
4419
  fontSize: "1.45rem",
4282
4420
  fontWeight: 700,
4283
4421
  letterSpacing: "-0.02em",
4284
4422
  color,
4285
4423
  margin: "20px 0 8px"
4286
4424
  });
4287
- var subtitleStyle9 = (color) => ({
4425
+ var subtitleStyle10 = (color) => ({
4288
4426
  fontSize: "0.9rem",
4289
4427
  color,
4290
4428
  margin: "0 0 24px",
@@ -4328,10 +4466,10 @@ function ConfirmSignScreen({
4328
4466
  ] }),
4329
4467
  children: [
4330
4468
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
4331
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle7, children: [
4469
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle8, children: [
4332
4470
  logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: displayName, style: logoStyle2 }) : /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
4333
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle10(tokens.text), children: "Wallet authorized" }),
4334
- /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle10(tokens.textSecondary), children: [
4471
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle11(tokens.text), children: "Wallet authorized" }),
4472
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle11(tokens.textSecondary), children: [
4335
4473
  displayName,
4336
4474
  " approved the connection. Tap below to confirm your payment."
4337
4475
  ] }),
@@ -4344,7 +4482,7 @@ function ConfirmSignScreen({
4344
4482
  }
4345
4483
  );
4346
4484
  }
4347
- var contentStyle7 = {
4485
+ var contentStyle8 = {
4348
4486
  flex: 1,
4349
4487
  display: "flex",
4350
4488
  flexDirection: "column",
@@ -4359,14 +4497,14 @@ var logoStyle2 = {
4359
4497
  borderRadius: 14,
4360
4498
  objectFit: "contain"
4361
4499
  };
4362
- var headingStyle10 = (color) => ({
4500
+ var headingStyle11 = (color) => ({
4363
4501
  fontSize: "1.45rem",
4364
4502
  fontWeight: 700,
4365
4503
  letterSpacing: "-0.02em",
4366
4504
  color,
4367
4505
  margin: "20px 0 8px"
4368
4506
  });
4369
- var subtitleStyle10 = (color) => ({
4507
+ var subtitleStyle11 = (color) => ({
4370
4508
  fontSize: "0.9rem",
4371
4509
  color,
4372
4510
  margin: "0 0 24px",
@@ -4425,7 +4563,7 @@ var PaymentErrorBoundary = class extends react.Component {
4425
4563
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 8v5", stroke: "#ef4444", strokeWidth: "1.5", strokeLinecap: "round" }),
4426
4564
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "16", r: "0.75", fill: "#ef4444" })
4427
4565
  ] }) }),
4428
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle11, children: "Something went wrong" }),
4566
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle12, children: "Something went wrong" }),
4429
4567
  /* @__PURE__ */ jsxRuntime.jsx("p", { style: messageStyle, children: "An unexpected error occurred. Please try again." }),
4430
4568
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: this.handleReset, style: buttonStyle3, children: "Try again" })
4431
4569
  ] });
@@ -4445,7 +4583,7 @@ var containerStyle8 = {
4445
4583
  var iconStyle4 = {
4446
4584
  marginBottom: 20
4447
4585
  };
4448
- var headingStyle11 = {
4586
+ var headingStyle12 = {
4449
4587
  fontSize: "1.25rem",
4450
4588
  fontWeight: 700,
4451
4589
  color: "#1a1a1a",
@@ -4608,6 +4746,7 @@ function SwypePaymentInner({
4608
4746
  const [transfer, setTransfer] = react.useState(null);
4609
4747
  const [creatingTransfer, setCreatingTransfer] = react.useState(false);
4610
4748
  const [registeringPasskey, setRegisteringPasskey] = react.useState(false);
4749
+ const [verifyingPasskeyPopup, setVerifyingPasskeyPopup] = react.useState(false);
4611
4750
  const [passkeyPopupNeeded, setPasskeyPopupNeeded] = react.useState(
4612
4751
  () => isSafari() && isInCrossOriginIframe()
4613
4752
  );
@@ -4916,11 +5055,17 @@ function SwypePaymentInner({
4916
5055
  }
4917
5056
  if (cancelled) return;
4918
5057
  const credentialIds = allPasskeys.map((p) => p.credentialId);
5058
+ if (isSafari() && isInCrossOriginIframe()) {
5059
+ setStep("verify-passkey");
5060
+ return;
5061
+ }
4919
5062
  const matched = await findDevicePasskey(credentialIds);
4920
5063
  if (cancelled) return;
4921
5064
  if (matched) {
4922
5065
  setActiveCredentialId(matched);
4923
5066
  window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, matched);
5067
+ reportPasskeyActivity(apiBaseUrl, token, matched).catch(() => {
5068
+ });
4924
5069
  await restoreOrDeposit(matched, token);
4925
5070
  return;
4926
5071
  }
@@ -5442,6 +5587,32 @@ function SwypePaymentInner({
5442
5587
  setRegisteringPasskey(false);
5443
5588
  }
5444
5589
  }, [user, completePasskeyRegistration, getAccessToken, apiBaseUrl, knownCredentialIds]);
5590
+ const handleVerifyPasskeyViaPopup = react.useCallback(async () => {
5591
+ setVerifyingPasskeyPopup(true);
5592
+ setError(null);
5593
+ try {
5594
+ const matched = await findDevicePasskeyViaPopup({
5595
+ credentialIds: knownCredentialIds,
5596
+ rpId: resolvePasskeyRpId()
5597
+ });
5598
+ if (matched) {
5599
+ setActiveCredentialId(matched);
5600
+ window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, matched);
5601
+ const token = await getAccessToken();
5602
+ if (token) {
5603
+ reportPasskeyActivity(apiBaseUrl, token, matched).catch(() => {
5604
+ });
5605
+ }
5606
+ setStep("login");
5607
+ } else {
5608
+ setStep("create-passkey");
5609
+ }
5610
+ } catch {
5611
+ setStep("create-passkey");
5612
+ } finally {
5613
+ setVerifyingPasskeyPopup(false);
5614
+ }
5615
+ }, [knownCredentialIds, getAccessToken, apiBaseUrl]);
5445
5616
  const handleSelectProvider = react.useCallback((providerId) => {
5446
5617
  setSelectedProviderId(providerId);
5447
5618
  setSelectedAccountId(null);
@@ -5573,6 +5744,17 @@ function SwypePaymentInner({
5573
5744
  if ((step === "login" || step === "otp-verify") && authenticated) {
5574
5745
  return /* @__PURE__ */ jsxRuntime.jsx(ScreenLayout, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { textAlign: "center", padding: "48px 0", flex: 1, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(Spinner, { label: "Verifying your passkey..." }) }) });
5575
5746
  }
5747
+ if (step === "verify-passkey") {
5748
+ return /* @__PURE__ */ jsxRuntime.jsx(
5749
+ VerifyPasskeyScreen,
5750
+ {
5751
+ onVerify: handleVerifyPasskeyViaPopup,
5752
+ onBack: handleLogout,
5753
+ verifying: verifyingPasskeyPopup,
5754
+ error
5755
+ }
5756
+ );
5757
+ }
5576
5758
  if (step === "create-passkey") {
5577
5759
  return /* @__PURE__ */ jsxRuntime.jsx(
5578
5760
  CreatePasskeyScreen,
@@ -5757,8 +5939,10 @@ exports.createPasskeyViaPopup = createPasskeyViaPopup;
5757
5939
  exports.darkTheme = darkTheme;
5758
5940
  exports.deviceHasPasskey = deviceHasPasskey;
5759
5941
  exports.findDevicePasskey = findDevicePasskey;
5942
+ exports.findDevicePasskeyViaPopup = findDevicePasskeyViaPopup;
5760
5943
  exports.getTheme = getTheme;
5761
5944
  exports.lightTheme = lightTheme;
5945
+ exports.resolvePasskeyRpId = resolvePasskeyRpId;
5762
5946
  exports.swypeApi = api_exports;
5763
5947
  exports.useAuthorizationExecutor = useAuthorizationExecutor;
5764
5948
  exports.useSwypeConfig = useSwypeConfig;