@swype-org/react-sdk 0.1.27 → 0.1.29

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
@@ -28,9 +28,9 @@ var darkTheme = {
28
28
  textMuted: "#7fa4b0",
29
29
  textInverse: "#052027",
30
30
  border: "#2b4551",
31
- borderFocus: "#2ec4cf",
32
- accent: "#2eb6c2",
33
- accentHover: "#239ba6",
31
+ borderFocus: "#28b67a",
32
+ accent: "#28b67a",
33
+ accentHover: "#219866",
34
34
  accentText: "#ffffff",
35
35
  success: "#0f9d73",
36
36
  successBg: "#0f2f2a",
@@ -52,9 +52,9 @@ var lightTheme = {
52
52
  textMuted: "#7d97a1",
53
53
  textInverse: "#ffffff",
54
54
  border: "#d2e4ea",
55
- borderFocus: "#2eb6c2",
56
- accent: "#2eb6c2",
57
- accentHover: "#259da7",
55
+ borderFocus: "#28b67a",
56
+ accent: "#28b67a",
57
+ accentHover: "#219866",
58
58
  accentText: "#ffffff",
59
59
  success: "#0f9d73",
60
60
  successBg: "#e6f7f1",
@@ -77,26 +77,6 @@ var wagmiConfig = wagmi.createConfig({
77
77
  [chains.base.id]: wagmi.http()
78
78
  }
79
79
  });
80
- var PRIVY_MODAL_CENTER_CSS = `
81
- @media (max-width: 440px) {
82
- #privy-dialog [data-headlessui-state] {
83
- position: static !important;
84
- bottom: auto !important;
85
- margin: auto !important;
86
- width: 360px !important;
87
- max-width: calc(100vw - 32px) !important;
88
- box-shadow: 0px 8px 36px rgba(55, 65, 81, 0.15) !important;
89
- border-radius: var(--privy-border-radius-lg) !important;
90
- transform: none !important;
91
- transition: opacity 100ms ease-in !important;
92
- }
93
- #privy-dialog [data-headlessui-state].entering,
94
- #privy-dialog [data-headlessui-state].leaving {
95
- opacity: 0 !important;
96
- transform: none !important;
97
- }
98
- }
99
- `;
100
80
  var SwypeContext = react.createContext(null);
101
81
  function SwypeProvider({
102
82
  apiBaseUrl,
@@ -107,15 +87,6 @@ function SwypeProvider({
107
87
  if (!queryClientRef.current) {
108
88
  queryClientRef.current = new reactQuery.QueryClient();
109
89
  }
110
- react.useEffect(() => {
111
- const style = document.createElement("style");
112
- style.setAttribute("data-swype", "privy-modal-center");
113
- style.textContent = PRIVY_MODAL_CENTER_CSS;
114
- document.head.appendChild(style);
115
- return () => {
116
- style.remove();
117
- };
118
- }, []);
119
90
  const [depositAmount, setDepositAmountRaw] = react.useState(null);
120
91
  const setDepositAmount = react.useCallback((amount) => {
121
92
  setDepositAmountRaw(amount);
@@ -138,6 +109,12 @@ function SwypeProvider({
138
109
  appearance: {
139
110
  theme,
140
111
  accentColor: getTheme(theme).accent
112
+ },
113
+ intl: {
114
+ defaultCountry: "US"
115
+ },
116
+ embeddedWallets: {
117
+ showWalletUIs: false
141
118
  }
142
119
  },
143
120
  children: /* @__PURE__ */ jsxRuntime.jsx(SwypeContext.Provider, { value, children })
@@ -176,6 +153,7 @@ __export(api_exports, {
176
153
  fetchAccounts: () => fetchAccounts,
177
154
  fetchAuthorizationSession: () => fetchAuthorizationSession,
178
155
  fetchChains: () => fetchChains,
156
+ fetchMerchantPublicKey: () => fetchMerchantPublicKey,
179
157
  fetchProviders: () => fetchProviders,
180
158
  fetchTransfer: () => fetchTransfer,
181
159
  fetchUserConfig: () => fetchUserConfig,
@@ -218,9 +196,13 @@ async function fetchAccounts(apiBaseUrl, token, credentialId) {
218
196
  return data.items;
219
197
  }
220
198
  async function createTransfer(apiBaseUrl, token, params) {
199
+ if (!params.merchantAuthorization) {
200
+ throw new Error("merchantAuthorization is required for transfer creation.");
201
+ }
221
202
  const body = {
222
- id: crypto.randomUUID(),
203
+ id: params.id ?? crypto.randomUUID(),
223
204
  credentialId: params.credentialId,
205
+ merchantAuthorization: params.merchantAuthorization,
224
206
  sources: [{ [params.sourceType]: params.sourceId }],
225
207
  destinations: [
226
208
  {
@@ -245,6 +227,13 @@ async function createTransfer(apiBaseUrl, token, params) {
245
227
  if (!res.ok) await throwApiError(res);
246
228
  return await res.json();
247
229
  }
230
+ async function fetchMerchantPublicKey(apiBaseUrl, merchantId) {
231
+ const res = await fetch(
232
+ `${apiBaseUrl}/v1/merchants/${encodeURIComponent(merchantId)}/public-key`
233
+ );
234
+ if (!res.ok) await throwApiError(res);
235
+ return await res.json();
236
+ }
248
237
  async function fetchTransfer(apiBaseUrl, token, transferId, authorizationSessionToken) {
249
238
  if (!token && !authorizationSessionToken) {
250
239
  throw new Error("Missing auth credentials for transfer fetch.");
@@ -816,6 +805,11 @@ async function createPasskeyCredential(params) {
816
805
  };
817
806
  }
818
807
  async function deviceHasPasskey(credentialId) {
808
+ const found = await findDevicePasskey([credentialId]);
809
+ return found != null;
810
+ }
811
+ async function findDevicePasskey(credentialIds) {
812
+ if (credentialIds.length === 0) return null;
819
813
  try {
820
814
  const challenge = new Uint8Array(32);
821
815
  crypto.getRandomValues(challenge);
@@ -824,17 +818,18 @@ async function deviceHasPasskey(credentialId) {
824
818
  publicKey: {
825
819
  challenge,
826
820
  rpId: resolvePasskeyRpId(),
827
- allowCredentials: [{
821
+ allowCredentials: credentialIds.map((id) => ({
828
822
  type: "public-key",
829
- id: base64ToBytes(credentialId)
830
- }],
823
+ id: base64ToBytes(id)
824
+ })),
831
825
  userVerification: "discouraged",
832
826
  timeout: 3e4
833
827
  }
834
828
  });
835
- return assertion != null;
829
+ if (!assertion) return null;
830
+ return toBase64(assertion.rawId);
836
831
  } catch {
837
- return false;
832
+ return null;
838
833
  }
839
834
  }
840
835
  function useTransferPolling(intervalMs = 3e3) {
@@ -2094,6 +2089,46 @@ function AdvancedSettings({
2094
2089
  ] });
2095
2090
  }
2096
2091
 
2092
+ // src/auth.ts
2093
+ var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2094
+ var PHONE_DIGIT_PATTERN = /^\d{7,15}$/;
2095
+ function normalizePhoneNumber(rawValue) {
2096
+ const trimmed = rawValue.trim();
2097
+ if (!trimmed) return null;
2098
+ const hasExplicitCountryCode = trimmed.startsWith("+");
2099
+ const digits = trimmed.replace(/\D/g, "");
2100
+ if (!PHONE_DIGIT_PATTERN.test(digits)) return null;
2101
+ return hasExplicitCountryCode ? `+${digits}` : digits;
2102
+ }
2103
+ function normalizeAuthIdentifier(rawValue) {
2104
+ const trimmed = rawValue.trim();
2105
+ if (!trimmed) return null;
2106
+ if (EMAIL_PATTERN.test(trimmed)) {
2107
+ return {
2108
+ kind: "email",
2109
+ value: trimmed.toLowerCase()
2110
+ };
2111
+ }
2112
+ const normalizedPhoneNumber = normalizePhoneNumber(trimmed);
2113
+ if (normalizedPhoneNumber) {
2114
+ return {
2115
+ kind: "phone",
2116
+ value: normalizedPhoneNumber
2117
+ };
2118
+ }
2119
+ return null;
2120
+ }
2121
+ function maskAuthIdentifier(identifier) {
2122
+ if (identifier.kind === "email") {
2123
+ const [localPart, domain = ""] = identifier.value.split("@");
2124
+ const localPrefix = localPart.slice(0, 2);
2125
+ return `${localPrefix}${"*".repeat(Math.max(localPart.length - 2, 0))}@${domain}`;
2126
+ }
2127
+ const digits = identifier.value.replace(/\D/g, "");
2128
+ const visibleSuffix = digits.slice(-4);
2129
+ return `***-***-${visibleSuffix}`;
2130
+ }
2131
+
2097
2132
  // src/processingStatus.ts
2098
2133
  var PROCESSING_TIMEOUT_MS = 18e4;
2099
2134
  function resolvePreferredTransfer(polledTransfer, localTransfer) {
@@ -2199,10 +2234,22 @@ function SwypePayment({
2199
2234
  destination,
2200
2235
  onComplete,
2201
2236
  onError,
2202
- useWalletConnector
2237
+ useWalletConnector,
2238
+ idempotencyKey,
2239
+ merchantAuthorization
2203
2240
  }) {
2204
2241
  const { apiBaseUrl, tokens, depositAmount } = useSwypeConfig();
2205
- const { ready, authenticated, user, login, logout, getAccessToken } = reactAuth.usePrivy();
2242
+ const { ready, authenticated, user, logout, getAccessToken } = reactAuth.usePrivy();
2243
+ const {
2244
+ sendCode: sendEmailCode,
2245
+ loginWithCode: loginWithEmailCode,
2246
+ state: emailLoginState
2247
+ } = reactAuth.useLoginWithEmail();
2248
+ const {
2249
+ sendCode: sendSmsCode,
2250
+ loginWithCode: loginWithSmsCode,
2251
+ state: smsLoginState
2252
+ } = reactAuth.useLoginWithSms();
2206
2253
  const [step, setStep] = react.useState("login");
2207
2254
  const [error, setError] = react.useState(null);
2208
2255
  const [providers, setProviders] = react.useState([]);
@@ -2227,6 +2274,11 @@ function SwypePayment({
2227
2274
  if (typeof window === "undefined") return null;
2228
2275
  return window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY);
2229
2276
  });
2277
+ const [authInput, setAuthInput] = react.useState("");
2278
+ const [verificationTarget, setVerificationTarget] = react.useState(
2279
+ null
2280
+ );
2281
+ const [otpCode, setOtpCode] = react.useState("");
2230
2282
  const [mobileFlow, setMobileFlow] = react.useState(false);
2231
2283
  const pollingTransferIdRef = react.useRef(null);
2232
2284
  const mobileSigningTransferIdRef = react.useRef(null);
@@ -2244,9 +2296,81 @@ function SwypePayment({
2244
2296
  setAmount(depositAmount.toString());
2245
2297
  }
2246
2298
  }, [depositAmount]);
2299
+ const resetHeadlessLogin = react.useCallback(() => {
2300
+ setAuthInput("");
2301
+ setVerificationTarget(null);
2302
+ setOtpCode("");
2303
+ }, []);
2304
+ const activeOtpStatus = verificationTarget?.kind === "email" ? emailLoginState.status : verificationTarget?.kind === "phone" ? smsLoginState.status : "initial";
2305
+ const activeOtpErrorMessage = verificationTarget?.kind === "email" && emailLoginState.status === "error" ? emailLoginState.error?.message ?? "Failed to continue with email." : verificationTarget?.kind === "phone" && smsLoginState.status === "error" ? smsLoginState.error?.message ?? "Failed to continue with phone number." : null;
2306
+ react.useEffect(() => {
2307
+ if (activeOtpErrorMessage) {
2308
+ setError(activeOtpErrorMessage);
2309
+ }
2310
+ }, [activeOtpErrorMessage]);
2311
+ const handleSendLoginCode = react.useCallback(async () => {
2312
+ const normalizedIdentifier = normalizeAuthIdentifier(authInput);
2313
+ if (!normalizedIdentifier) {
2314
+ setError("Enter a valid email address or phone number.");
2315
+ return;
2316
+ }
2317
+ setError(null);
2318
+ setOtpCode("");
2319
+ try {
2320
+ if (normalizedIdentifier.kind === "email") {
2321
+ await sendEmailCode({ email: normalizedIdentifier.value });
2322
+ } else {
2323
+ await sendSmsCode({ phoneNumber: normalizedIdentifier.value });
2324
+ }
2325
+ setVerificationTarget(normalizedIdentifier);
2326
+ } catch (err) {
2327
+ const msg = err instanceof Error ? err.message : "Failed to send verification code";
2328
+ setError(msg);
2329
+ }
2330
+ }, [authInput, sendEmailCode, sendSmsCode]);
2331
+ const handleVerifyLoginCode = react.useCallback(async () => {
2332
+ if (!verificationTarget) return;
2333
+ const trimmedCode = otpCode.trim();
2334
+ if (!/^\d{6}$/.test(trimmedCode)) {
2335
+ setError("Enter the 6-digit verification code.");
2336
+ return;
2337
+ }
2338
+ setError(null);
2339
+ try {
2340
+ if (verificationTarget.kind === "email") {
2341
+ await loginWithEmailCode({ code: trimmedCode });
2342
+ } else {
2343
+ await loginWithSmsCode({ code: trimmedCode });
2344
+ }
2345
+ } catch (err) {
2346
+ const msg = err instanceof Error ? err.message : "Failed to verify code";
2347
+ setError(msg);
2348
+ }
2349
+ }, [verificationTarget, otpCode, loginWithEmailCode, loginWithSmsCode]);
2350
+ const handleResendLoginCode = react.useCallback(async () => {
2351
+ if (!verificationTarget) return;
2352
+ setError(null);
2353
+ try {
2354
+ if (verificationTarget.kind === "email") {
2355
+ await sendEmailCode({ email: verificationTarget.value });
2356
+ } else {
2357
+ await sendSmsCode({ phoneNumber: verificationTarget.value });
2358
+ }
2359
+ } catch (err) {
2360
+ const msg = err instanceof Error ? err.message : "Failed to resend code";
2361
+ setError(msg);
2362
+ }
2363
+ }, [verificationTarget, sendEmailCode, sendSmsCode]);
2364
+ const handleEditIdentifier = react.useCallback(() => {
2365
+ setError(null);
2366
+ setVerificationTarget(null);
2367
+ setOtpCode("");
2368
+ }, []);
2247
2369
  react.useEffect(() => {
2248
2370
  if (!ready || !authenticated || step !== "login") return;
2249
2371
  let cancelled = false;
2372
+ setError(null);
2373
+ resetHeadlessLogin();
2250
2374
  const checkPasskey = async () => {
2251
2375
  try {
2252
2376
  const token = await getAccessToken();
@@ -2266,20 +2390,20 @@ function SwypePayment({
2266
2390
  }
2267
2391
  return;
2268
2392
  }
2269
- for (const pk of allPasskeys) {
2270
- if (cancelled) return;
2271
- if (await deviceHasPasskey(pk.credentialId)) {
2272
- setActiveCredentialId(pk.credentialId);
2273
- window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, pk.credentialId);
2274
- if (depositAmount != null && depositAmount > 0) {
2275
- setStep("ready");
2276
- } else {
2277
- setStep("enter-amount");
2278
- }
2279
- return;
2393
+ if (cancelled) return;
2394
+ const credentialIds = allPasskeys.map((p) => p.credentialId);
2395
+ const matched = await findDevicePasskey(credentialIds);
2396
+ if (cancelled) return;
2397
+ if (matched) {
2398
+ setActiveCredentialId(matched);
2399
+ window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, matched);
2400
+ if (depositAmount != null && depositAmount > 0) {
2401
+ setStep("ready");
2402
+ } else {
2403
+ setStep("enter-amount");
2280
2404
  }
2405
+ return;
2281
2406
  }
2282
- if (cancelled) return;
2283
2407
  setStep("register-passkey");
2284
2408
  } catch {
2285
2409
  if (!cancelled) {
@@ -2295,7 +2419,16 @@ function SwypePayment({
2295
2419
  return () => {
2296
2420
  cancelled = true;
2297
2421
  };
2298
- }, [ready, authenticated, step, depositAmount, apiBaseUrl, getAccessToken, activeCredentialId]);
2422
+ }, [
2423
+ ready,
2424
+ authenticated,
2425
+ step,
2426
+ depositAmount,
2427
+ apiBaseUrl,
2428
+ getAccessToken,
2429
+ activeCredentialId,
2430
+ resetHeadlessLogin
2431
+ ]);
2299
2432
  const loadingDataRef = react.useRef(false);
2300
2433
  react.useEffect(() => {
2301
2434
  if (!authenticated) return;
@@ -2513,7 +2646,9 @@ function SwypePayment({
2513
2646
  }
2514
2647
  }
2515
2648
  const t = await createTransfer(apiBaseUrl, token, {
2649
+ id: idempotencyKey,
2516
2650
  credentialId: activeCredentialId,
2651
+ merchantAuthorization,
2517
2652
  sourceType: effectiveSourceType,
2518
2653
  sourceId: effectiveSourceId,
2519
2654
  destination,
@@ -2603,7 +2738,8 @@ function SwypePayment({
2603
2738
  processingStartedAtRef.current = null;
2604
2739
  pollingTransferIdRef.current = null;
2605
2740
  mobileSigningTransferIdRef.current = null;
2606
- }, [logout, polling, depositAmount]);
2741
+ resetHeadlessLogin();
2742
+ }, [logout, polling, depositAmount, resetHeadlessLogin]);
2607
2743
  const handleConnectNewAccount = (providerId) => {
2608
2744
  setSelectedProviderId(providerId);
2609
2745
  setSelectedAccountId(null);
@@ -2640,7 +2776,7 @@ function SwypePayment({
2640
2776
  cursor: "pointer",
2641
2777
  transition: "filter 0.15s ease, transform 0.15s ease",
2642
2778
  fontFamily: "inherit",
2643
- boxShadow: "0 8px 18px rgba(46, 182, 194, 0.28)"
2779
+ boxShadow: "0 8px 18px rgba(40, 182, 122, 0.28)"
2644
2780
  };
2645
2781
  const btnDisabled = {
2646
2782
  ...btnPrimary,
@@ -2652,6 +2788,33 @@ function SwypePayment({
2652
2788
  background: tokens.bgCard,
2653
2789
  color: tokens.textSecondary,
2654
2790
  border: `1px solid ${tokens.border}`});
2791
+ const textFieldStyle = {
2792
+ width: "100%",
2793
+ padding: "15px 16px",
2794
+ borderRadius: "16px",
2795
+ border: `1px solid ${tokens.border}`,
2796
+ background: tokens.bgInput,
2797
+ color: tokens.text,
2798
+ fontSize: "0.98rem",
2799
+ fontFamily: "inherit",
2800
+ outline: "none",
2801
+ boxSizing: "border-box"
2802
+ };
2803
+ const authCaptionStyle = {
2804
+ fontSize: "0.84rem",
2805
+ color: tokens.textSecondary,
2806
+ margin: 0,
2807
+ lineHeight: 1.5
2808
+ };
2809
+ const authTertiaryButtonStyle = {
2810
+ background: "transparent",
2811
+ border: "none",
2812
+ color: tokens.textMuted,
2813
+ cursor: "pointer",
2814
+ fontFamily: "inherit",
2815
+ fontSize: "0.84rem",
2816
+ padding: 0
2817
+ };
2655
2818
  const errorStyle = {
2656
2819
  background: tokens.errorBg,
2657
2820
  border: `1px solid ${tokens.error}66`,
@@ -2712,6 +2875,7 @@ function SwypePayment({
2712
2875
  ]
2713
2876
  }
2714
2877
  );
2878
+ const placeholderProviders = ["A", "B", "C", "D", "E"];
2715
2879
  const displayedSelectSourceChoices = selectSourceChoices.length > 0 ? selectSourceChoices : [
2716
2880
  {
2717
2881
  chainName: "Base",
@@ -2747,43 +2911,186 @@ function SwypePayment({
2747
2911
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: cardStyle, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { textAlign: "center", padding: "24px 0" }, children: /* @__PURE__ */ jsxRuntime.jsx(Spinner, { label: "Initializing..." }) }) });
2748
2912
  }
2749
2913
  if (step === "login" && !authenticated) {
2914
+ const isAwaitingOtp = verificationTarget !== null;
2915
+ const isSendingCode = activeOtpStatus === "sending-code";
2916
+ const isSubmittingCode = activeOtpStatus === "submitting-code";
2917
+ const continueDisabled = authInput.trim().length === 0 || isSendingCode || isSubmittingCode;
2918
+ const verifyDisabled = otpCode.trim().length !== 6 || isSubmittingCode;
2750
2919
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: cardStyle, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center" }, children: [
2751
- /* @__PURE__ */ jsxRuntime.jsxs(
2752
- "svg",
2920
+ /* @__PURE__ */ jsxRuntime.jsx(
2921
+ "div",
2753
2922
  {
2754
- width: "48",
2755
- height: "48",
2756
- viewBox: "0 0 48 48",
2757
- fill: "none",
2758
- style: { margin: "0 auto 16px" },
2759
- children: [
2760
- /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "48", height: "48", rx: "12", fill: tokens.accent + "20" }),
2761
- /* @__PURE__ */ jsxRuntime.jsx(
2762
- "path",
2763
- {
2764
- d: "M24 14v20M14 24h20",
2765
- stroke: tokens.accent,
2766
- strokeWidth: "2.5",
2767
- strokeLinecap: "round"
2768
- }
2769
- )
2770
- ]
2923
+ style: {
2924
+ width: 56,
2925
+ height: 56,
2926
+ borderRadius: 14,
2927
+ background: tokens.accent,
2928
+ color: tokens.accentText,
2929
+ display: "flex",
2930
+ alignItems: "center",
2931
+ justifyContent: "center",
2932
+ fontWeight: 700,
2933
+ fontSize: "1.35rem",
2934
+ margin: "0 auto 24px",
2935
+ boxShadow: "0 10px 20px rgba(40, 182, 122, 0.22)"
2936
+ },
2937
+ children: "S"
2938
+ }
2939
+ ),
2940
+ /* @__PURE__ */ jsxRuntime.jsx(
2941
+ "h2",
2942
+ {
2943
+ style: {
2944
+ ...headingStyle,
2945
+ fontSize: "2.05rem",
2946
+ lineHeight: 1.15,
2947
+ marginBottom: "10px",
2948
+ whiteSpace: "pre-line"
2949
+ },
2950
+ children: isAwaitingOtp ? "Enter your code." : "One-time setup.\nOne-tap deposits after."
2771
2951
  }
2772
2952
  ),
2773
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { ...headingStyle, marginBottom: "8px" }, children: "Pay with Swype" }),
2774
2953
  /* @__PURE__ */ jsxRuntime.jsx(
2775
2954
  "p",
2776
2955
  {
2777
2956
  style: {
2778
- fontSize: "0.875rem",
2779
- color: tokens.textSecondary,
2780
- margin: "0 0 24px 0",
2781
- lineHeight: 1.5
2957
+ ...authCaptionStyle,
2958
+ margin: "0 0 26px 0",
2959
+ whiteSpace: "pre-line"
2782
2960
  },
2783
- children: "Connect your account to continue"
2961
+ children: isAwaitingOtp ? `We sent a 6-digit code to ${maskAuthIdentifier(verificationTarget)}.` : "Protected by Face ID."
2784
2962
  }
2785
2963
  ),
2786
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: btnPrimary, onClick: login, children: "Connect to Swype" })
2964
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorStyle, children: error }),
2965
+ isAwaitingOtp ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2966
+ /* @__PURE__ */ jsxRuntime.jsx(
2967
+ "input",
2968
+ {
2969
+ id: "swype-login-code",
2970
+ type: "text",
2971
+ inputMode: "numeric",
2972
+ autoComplete: "one-time-code",
2973
+ placeholder: "Verification code",
2974
+ value: otpCode,
2975
+ onChange: (event) => {
2976
+ setOtpCode(event.target.value.replace(/\D/g, "").slice(0, 6));
2977
+ },
2978
+ style: {
2979
+ ...textFieldStyle,
2980
+ textAlign: "center",
2981
+ letterSpacing: "0.24em",
2982
+ marginBottom: "14px"
2983
+ }
2984
+ }
2985
+ ),
2986
+ /* @__PURE__ */ jsxRuntime.jsx(
2987
+ "button",
2988
+ {
2989
+ style: verifyDisabled ? btnDisabled : btnPrimary,
2990
+ disabled: verifyDisabled,
2991
+ onClick: handleVerifyLoginCode,
2992
+ children: isSubmittingCode ? "Verifying..." : "Continue"
2993
+ }
2994
+ ),
2995
+ /* @__PURE__ */ jsxRuntime.jsxs(
2996
+ "div",
2997
+ {
2998
+ style: {
2999
+ display: "flex",
3000
+ justifyContent: "space-between",
3001
+ gap: "12px",
3002
+ marginTop: "14px"
3003
+ },
3004
+ children: [
3005
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: authTertiaryButtonStyle, onClick: handleEditIdentifier, children: "Use a different email or phone" }),
3006
+ /* @__PURE__ */ jsxRuntime.jsx(
3007
+ "button",
3008
+ {
3009
+ type: "button",
3010
+ style: authTertiaryButtonStyle,
3011
+ onClick: handleResendLoginCode,
3012
+ disabled: isSendingCode || isSubmittingCode,
3013
+ children: isSendingCode ? "Sending..." : "Resend code"
3014
+ }
3015
+ )
3016
+ ]
3017
+ }
3018
+ )
3019
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3020
+ /* @__PURE__ */ jsxRuntime.jsx(
3021
+ "input",
3022
+ {
3023
+ id: "swype-login-identifier",
3024
+ type: "text",
3025
+ inputMode: "text",
3026
+ autoComplete: "username",
3027
+ placeholder: "Email or phone number",
3028
+ value: authInput,
3029
+ onChange: (event) => setAuthInput(event.target.value),
3030
+ style: { ...textFieldStyle, marginBottom: "14px" }
3031
+ }
3032
+ ),
3033
+ /* @__PURE__ */ jsxRuntime.jsx(
3034
+ "button",
3035
+ {
3036
+ style: continueDisabled ? btnDisabled : btnPrimary,
3037
+ disabled: continueDisabled,
3038
+ onClick: handleSendLoginCode,
3039
+ children: isSendingCode ? "Sending code..." : "Continue"
3040
+ }
3041
+ ),
3042
+ /* @__PURE__ */ jsxRuntime.jsxs(
3043
+ "div",
3044
+ {
3045
+ style: {
3046
+ display: "flex",
3047
+ alignItems: "center",
3048
+ gap: "10px",
3049
+ margin: "22px 0 14px",
3050
+ color: tokens.textMuted,
3051
+ fontSize: "0.82rem"
3052
+ },
3053
+ children: [
3054
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, height: 1, background: tokens.border } }),
3055
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "works with" }),
3056
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, height: 1, background: tokens.border } })
3057
+ ]
3058
+ }
3059
+ ),
3060
+ /* @__PURE__ */ jsxRuntime.jsx(
3061
+ "div",
3062
+ {
3063
+ "aria-label": "Works with placeholder providers",
3064
+ style: {
3065
+ display: "flex",
3066
+ justifyContent: "center",
3067
+ gap: "12px",
3068
+ marginBottom: "18px"
3069
+ },
3070
+ children: placeholderProviders.map((providerLabel) => /* @__PURE__ */ jsxRuntime.jsx(
3071
+ "div",
3072
+ {
3073
+ style: {
3074
+ width: 34,
3075
+ height: 34,
3076
+ borderRadius: 999,
3077
+ border: `1px solid ${tokens.border}`,
3078
+ background: tokens.bgInput,
3079
+ color: tokens.textMuted,
3080
+ display: "flex",
3081
+ alignItems: "center",
3082
+ justifyContent: "center",
3083
+ fontSize: "0.78rem",
3084
+ fontWeight: 600
3085
+ },
3086
+ children: providerLabel
3087
+ },
3088
+ providerLabel
3089
+ ))
3090
+ }
3091
+ ),
3092
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { ...authCaptionStyle, color: tokens.textMuted }, children: "Powered by Swype. Non-custodial." })
3093
+ ] })
2787
3094
  ] }) });
2788
3095
  }
2789
3096
  if (step === "register-passkey") {
@@ -3551,6 +3858,7 @@ exports.SwypeProvider = SwypeProvider;
3551
3858
  exports.createPasskeyCredential = createPasskeyCredential;
3552
3859
  exports.darkTheme = darkTheme;
3553
3860
  exports.deviceHasPasskey = deviceHasPasskey;
3861
+ exports.findDevicePasskey = findDevicePasskey;
3554
3862
  exports.getTheme = getTheme;
3555
3863
  exports.lightTheme = lightTheme;
3556
3864
  exports.swypeApi = api_exports;