decane-connect-kit 2.2.4 → 2.2.6

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.d.cts CHANGED
@@ -387,7 +387,7 @@ interface SignTypedDataRequest {
387
387
  message: Record<string, unknown>;
388
388
  primaryType?: string;
389
389
  }
390
- type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started';
390
+ type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started' | 'wallet-creating' | 'wallet-unlocking';
391
391
  interface DecaneConnect {
392
392
  connect(): Promise<ConnectResult>;
393
393
  connectWithGoogle(): Promise<ConnectResult>;
@@ -730,6 +730,12 @@ interface SocialAuthState {
730
730
  * user, so signing back in costs one passkey prompt and nothing else.
731
731
  */
732
732
  canUsePasskey: boolean;
733
+ /**
734
+ * Long-running work in progress, so the UI can show a status screen instead
735
+ * of appearing frozen. 'creating' = first-time key generation (several
736
+ * seconds); 'unlocking' = reopening the TEE session for a returning user.
737
+ */
738
+ phase: "creating" | "unlocking" | null;
733
739
  }
734
740
  interface DecaneKitProps {
735
741
  config?: DecaneConfig;
@@ -752,6 +758,9 @@ interface SocialWalletModalProps {
752
758
  authMethods?: Array<"google" | "email">;
753
759
  customAuthLabel?: string;
754
760
  onCustomAuthSignIn?: () => Promise<void>;
761
+ canUsePasskey?: boolean;
762
+ passkeyAddress?: string | null;
763
+ onPasskeySignIn?: () => Promise<unknown>;
755
764
  onRotateShares: (opts: {
756
765
  password: string;
757
766
  passwordHint?: string;
@@ -763,7 +772,7 @@ interface SocialWalletModalProps {
763
772
  rpcUrls?: Record<string, string>;
764
773
  chains?: string[];
765
774
  }
766
- declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
775
+ declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, canUsePasskey, passkeyAddress, onPasskeySignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
767
776
 
768
777
  declare function DecaneMark({ size, muted }: {
769
778
  size?: number;
@@ -787,6 +796,9 @@ declare function useSocialAuth(): {
787
796
  solana?: string;
788
797
  } | null;
789
798
  sessionExpiresAt: number | null;
799
+ canUsePasskey: boolean;
800
+ phase: "creating" | "unlocking" | null;
801
+ isCreatingWallet: boolean;
790
802
  needsReconnect: boolean;
791
803
  googleLoading: boolean;
792
804
  emailLoading: boolean;
@@ -804,6 +816,7 @@ declare function useSocialAuth(): {
804
816
  providerId?: string;
805
817
  }) => Promise<void>;
806
818
  signInWithCustomAuth: (() => Promise<void>) | undefined;
819
+ signInWithPasskey: () => Promise<void>;
807
820
  unlock: () => Promise<void>;
808
821
  disconnect: () => Promise<void>;
809
822
  };
@@ -817,6 +830,8 @@ declare function useSocialWallet(): {
817
830
  } | null;
818
831
  sessionExpiresAt: number | null;
819
832
  needsReconnect: boolean;
833
+ phase: "creating" | "unlocking" | null;
834
+ isCreatingWallet: boolean;
820
835
  modalOpen: boolean;
821
836
  openModal: () => void;
822
837
  closeModal: () => void;
package/dist/index.d.ts CHANGED
@@ -387,7 +387,7 @@ interface SignTypedDataRequest {
387
387
  message: Record<string, unknown>;
388
388
  primaryType?: string;
389
389
  }
390
- type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started';
390
+ type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started' | 'wallet-creating' | 'wallet-unlocking';
391
391
  interface DecaneConnect {
392
392
  connect(): Promise<ConnectResult>;
393
393
  connectWithGoogle(): Promise<ConnectResult>;
@@ -730,6 +730,12 @@ interface SocialAuthState {
730
730
  * user, so signing back in costs one passkey prompt and nothing else.
731
731
  */
732
732
  canUsePasskey: boolean;
733
+ /**
734
+ * Long-running work in progress, so the UI can show a status screen instead
735
+ * of appearing frozen. 'creating' = first-time key generation (several
736
+ * seconds); 'unlocking' = reopening the TEE session for a returning user.
737
+ */
738
+ phase: "creating" | "unlocking" | null;
733
739
  }
734
740
  interface DecaneKitProps {
735
741
  config?: DecaneConfig;
@@ -752,6 +758,9 @@ interface SocialWalletModalProps {
752
758
  authMethods?: Array<"google" | "email">;
753
759
  customAuthLabel?: string;
754
760
  onCustomAuthSignIn?: () => Promise<void>;
761
+ canUsePasskey?: boolean;
762
+ passkeyAddress?: string | null;
763
+ onPasskeySignIn?: () => Promise<unknown>;
755
764
  onRotateShares: (opts: {
756
765
  password: string;
757
766
  passwordHint?: string;
@@ -763,7 +772,7 @@ interface SocialWalletModalProps {
763
772
  rpcUrls?: Record<string, string>;
764
773
  chains?: string[];
765
774
  }
766
- declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
775
+ declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, canUsePasskey, passkeyAddress, onPasskeySignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
767
776
 
768
777
  declare function DecaneMark({ size, muted }: {
769
778
  size?: number;
@@ -787,6 +796,9 @@ declare function useSocialAuth(): {
787
796
  solana?: string;
788
797
  } | null;
789
798
  sessionExpiresAt: number | null;
799
+ canUsePasskey: boolean;
800
+ phase: "creating" | "unlocking" | null;
801
+ isCreatingWallet: boolean;
790
802
  needsReconnect: boolean;
791
803
  googleLoading: boolean;
792
804
  emailLoading: boolean;
@@ -804,6 +816,7 @@ declare function useSocialAuth(): {
804
816
  providerId?: string;
805
817
  }) => Promise<void>;
806
818
  signInWithCustomAuth: (() => Promise<void>) | undefined;
819
+ signInWithPasskey: () => Promise<void>;
807
820
  unlock: () => Promise<void>;
808
821
  disconnect: () => Promise<void>;
809
822
  };
@@ -817,6 +830,8 @@ declare function useSocialWallet(): {
817
830
  } | null;
818
831
  sessionExpiresAt: number | null;
819
832
  needsReconnect: boolean;
833
+ phase: "creating" | "unlocking" | null;
834
+ isCreatingWallet: boolean;
820
835
  modalOpen: boolean;
821
836
  openModal: () => void;
822
837
  closeModal: () => void;
package/dist/index.js CHANGED
@@ -2548,9 +2548,6 @@ function validatePin(pin) {
2548
2548
  // src/social/crypto/passkey.ts
2549
2549
  init_errors();
2550
2550
  import { startRegistration, startAuthentication as startAuthentication2 } from "@simplewebauthn/browser";
2551
- function b64u(u) {
2552
- return btoa(String.fromCharCode(...u)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
2553
- }
2554
2551
  function fromB64u(s) {
2555
2552
  return Uint8Array.from(atob(s.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
2556
2553
  }
@@ -2585,7 +2582,7 @@ async function authenticateWithPasskey(opts) {
2585
2582
  ...opts.optionsJSON,
2586
2583
  extensions: {
2587
2584
  ...opts.optionsJSON.extensions,
2588
- prf: { eval: { first: b64u(opts.prfSalt) } }
2585
+ prf: { eval: { first: buf3(opts.prfSalt) } }
2589
2586
  }
2590
2587
  };
2591
2588
  let response;
@@ -3268,6 +3265,7 @@ async function createDecaneConnect(config) {
3268
3265
  }
3269
3266
  async function passkeyLogin(stored) {
3270
3267
  const userId = stored.userId;
3268
+ events.emit("wallet-unlocking");
3271
3269
  const options = await api.getWebAuthnLoginOptions(userId, rpId);
3272
3270
  if (!options) {
3273
3271
  throw new NewDeviceError();
@@ -3301,8 +3299,10 @@ async function createDecaneConnect(config) {
3301
3299
  async function handleAuthResult(result) {
3302
3300
  let addresses;
3303
3301
  if (!result.hasShare) {
3302
+ events.emit("wallet-creating");
3304
3303
  addresses = await firstTimeSetup(result.userId, result.jwt);
3305
3304
  } else {
3305
+ events.emit("wallet-unlocking");
3306
3306
  addresses = await returningUserUnlock(result.userId, result.jwt);
3307
3307
  }
3308
3308
  auth = { jwt: result.jwt, userId: result.userId, addresses };
@@ -3641,8 +3641,8 @@ async function createDecaneConnect(config) {
3641
3641
  function bufferToB64u(buf6) {
3642
3642
  return btoa(String.fromCharCode(...buf6)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
3643
3643
  }
3644
- function b64uToBuffer(b64u2) {
3645
- const b644 = b64u2.replace(/-/g, "+").replace(/_/g, "/");
3644
+ function b64uToBuffer(b64u) {
3645
+ const b644 = b64u.replace(/-/g, "+").replace(/_/g, "/");
3646
3646
  return Uint8Array.from(atob(b644), (c) => c.charCodeAt(0));
3647
3647
  }
3648
3648
  function toHex(bytes) {
@@ -4817,7 +4817,10 @@ function SignInView({
4817
4817
  authMethods,
4818
4818
  resumeHint,
4819
4819
  customAuthLabel,
4820
- onCustomAuthSignIn
4820
+ onCustomAuthSignIn,
4821
+ canUsePasskey,
4822
+ passkeyAddress,
4823
+ onPasskeySignIn
4821
4824
  }) {
4822
4825
  const [googleLoading, setGoogleLoading] = useState2(false);
4823
4826
  const [email, setEmail] = useState2("");
@@ -4825,7 +4828,25 @@ function SignInView({
4825
4828
  const [emailSent, setEmailSent] = useState2(false);
4826
4829
  const [emailLoading, setEmailLoading] = useState2(false);
4827
4830
  const [customLoading, setCustomLoading] = useState2(false);
4831
+ const [passkeyLoading, setPasskeyLoading] = useState2(false);
4832
+ const [showOtherMethods, setShowOtherMethods] = useState2(false);
4828
4833
  const [error, setError] = useState2("");
4834
+ const passkeyFirst = !!canUsePasskey && !!onPasskeySignIn && !showOtherMethods;
4835
+ async function handlePasskey() {
4836
+ if (!onPasskeySignIn) return;
4837
+ setError("");
4838
+ setPasskeyLoading(true);
4839
+ try {
4840
+ await onPasskeySignIn();
4841
+ } catch (e) {
4842
+ const err = e;
4843
+ if (err?.name !== "UserCancelledError") {
4844
+ setError(err?.message ?? "Passkey sign-in failed");
4845
+ setShowOtherMethods(true);
4846
+ }
4847
+ setPasskeyLoading(false);
4848
+ }
4849
+ }
4829
4850
  async function handleGoogle() {
4830
4851
  setError("");
4831
4852
  setGoogleLoading(true);
@@ -4885,7 +4906,47 @@ function SignInView({
4885
4906
  fontFamily: "inherit",
4886
4907
  boxSizing: "border-box"
4887
4908
  };
4888
- return /* @__PURE__ */ React3.createElement("div", { style: { padding: "0 16px 16px" } }, resumeHint && /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 12, color: "var(--dc-text-muted)", marginBottom: 12, textAlign: "center" } }, "Welcome back \u2014 sign in again to resume ", resumeHint), authMethods.includes("google") && /* @__PURE__ */ React3.createElement(
4909
+ return /* @__PURE__ */ React3.createElement("div", { style: { padding: "0 16px 16px" } }, resumeHint && !passkeyFirst && /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 12, color: "var(--dc-text-muted)", marginBottom: 12, textAlign: "center" } }, "Welcome back \u2014 sign in again to resume ", resumeHint), passkeyFirst && /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 12, color: "var(--dc-text-muted)", marginBottom: 12, textAlign: "center" } }, "Welcome back \u2014 unlock to resume", passkeyAddress ? ` ${passkeyAddress.slice(0, 6)}\u2026${passkeyAddress.slice(-4)}` : ""), /* @__PURE__ */ React3.createElement(
4910
+ "button",
4911
+ {
4912
+ onClick: handlePasskey,
4913
+ disabled: passkeyLoading,
4914
+ style: {
4915
+ width: "100%",
4916
+ padding: "11px 0",
4917
+ background: "var(--dc-accent)",
4918
+ color: "var(--dc-accent-text)",
4919
+ border: "none",
4920
+ borderRadius: 10,
4921
+ fontSize: 13,
4922
+ fontWeight: 700,
4923
+ cursor: passkeyLoading ? "not-allowed" : "pointer",
4924
+ opacity: passkeyLoading ? 0.7 : 1,
4925
+ fontFamily: "inherit",
4926
+ display: "flex",
4927
+ alignItems: "center",
4928
+ justifyContent: "center",
4929
+ gap: 7
4930
+ }
4931
+ },
4932
+ passkeyLoading ? "Verifying\u2026" : "\u{1F513} Continue with passkey"
4933
+ ), /* @__PURE__ */ React3.createElement(
4934
+ "button",
4935
+ {
4936
+ onClick: () => setShowOtherMethods(true),
4937
+ style: {
4938
+ width: "100%",
4939
+ marginTop: 10,
4940
+ background: "none",
4941
+ border: "none",
4942
+ color: "var(--dc-text-muted)",
4943
+ fontSize: 12,
4944
+ cursor: "pointer",
4945
+ fontFamily: "inherit"
4946
+ }
4947
+ },
4948
+ "Use email or Google instead"
4949
+ ), error && /* @__PURE__ */ React3.createElement("div", { style: { marginTop: 10, fontSize: 12, color: "var(--dc-danger, #ff5533)" } }, error)), !passkeyFirst && authMethods.includes("google") && /* @__PURE__ */ React3.createElement(
4889
4950
  "button",
4890
4951
  {
4891
4952
  onClick: handleGoogle,
@@ -4906,7 +4967,7 @@ function SignInView({
4906
4967
  }
4907
4968
  },
4908
4969
  googleLoading ? "Redirecting\u2026" : "Continue with Google"
4909
- ), authMethods.includes("email") && /* @__PURE__ */ React3.createElement(React3.Fragment, null, authMethods.includes("google") && /* @__PURE__ */ React3.createElement("div", { style: { textAlign: "center", color: "var(--dc-text-muted)", fontSize: 12, margin: "10px 0" } }, "\u2014 or \u2014"), !emailSent ? /* @__PURE__ */ React3.createElement("form", { onSubmit: handleEmailStart, style: { display: "flex", gap: 8 } }, /* @__PURE__ */ React3.createElement(
4970
+ ), !passkeyFirst && authMethods.includes("email") && /* @__PURE__ */ React3.createElement(React3.Fragment, null, authMethods.includes("google") && /* @__PURE__ */ React3.createElement("div", { style: { textAlign: "center", color: "var(--dc-text-muted)", fontSize: 12, margin: "10px 0" } }, "\u2014 or \u2014"), !emailSent ? /* @__PURE__ */ React3.createElement("form", { onSubmit: handleEmailStart, style: { display: "flex", gap: 8 } }, /* @__PURE__ */ React3.createElement(
4910
4971
  "input",
4911
4972
  {
4912
4973
  type: "email",
@@ -4945,7 +5006,7 @@ function SignInView({
4945
5006
  fontWeight: 700,
4946
5007
  cursor: "pointer",
4947
5008
  fontFamily: "inherit"
4948
- } }, emailLoading ? "\u2026" : "Verify"))), customAuthLabel && onCustomAuthSignIn && /* @__PURE__ */ React3.createElement(React3.Fragment, null, (authMethods.includes("google") || authMethods.includes("email")) && /* @__PURE__ */ React3.createElement("div", { style: { textAlign: "center", color: "var(--dc-text-muted)", fontSize: 12, margin: "10px 0" } }, "\u2014 or \u2014"), /* @__PURE__ */ React3.createElement(
5009
+ } }, emailLoading ? "\u2026" : "Verify"))), !passkeyFirst && customAuthLabel && onCustomAuthSignIn && /* @__PURE__ */ React3.createElement(React3.Fragment, null, (authMethods.includes("google") || authMethods.includes("email")) && /* @__PURE__ */ React3.createElement("div", { style: { textAlign: "center", color: "var(--dc-text-muted)", fontSize: 12, margin: "10px 0" } }, "\u2014 or \u2014"), /* @__PURE__ */ React3.createElement(
4949
5010
  "button",
4950
5011
  {
4951
5012
  onClick: handleCustomAuth,
@@ -5084,6 +5145,9 @@ function SocialWalletModal({
5084
5145
  authMethods = ["google", "email"],
5085
5146
  customAuthLabel,
5086
5147
  onCustomAuthSignIn,
5148
+ canUsePasskey,
5149
+ passkeyAddress,
5150
+ onPasskeySignIn,
5087
5151
  onRotateShares,
5088
5152
  onExportPortableBackup,
5089
5153
  rpcUrls,
@@ -5214,7 +5278,10 @@ function SocialWalletModal({
5214
5278
  authMethods,
5215
5279
  resumeHint: socialAuth.needsReconnect ? "your wallet" : void 0,
5216
5280
  customAuthLabel,
5217
- onCustomAuthSignIn
5281
+ onCustomAuthSignIn,
5282
+ canUsePasskey,
5283
+ passkeyAddress,
5284
+ onPasskeySignIn
5218
5285
  }
5219
5286
  ) : /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("div", { style: {
5220
5287
  margin: "0 16px 14px",
@@ -5301,21 +5368,103 @@ function SocialWalletModal({
5301
5368
  }
5302
5369
 
5303
5370
  // src/ui/context.tsx
5304
- import React4, {
5371
+ import React5, {
5305
5372
  createContext,
5306
5373
  useCallback as useCallback3,
5307
5374
  useContext,
5308
- useEffect as useEffect3,
5375
+ useEffect as useEffect4,
5309
5376
  useRef,
5310
5377
  useState as useState3
5311
5378
  } from "react";
5379
+
5380
+ // src/ui/StatusOverlay.tsx
5381
+ import React4, { useEffect as useEffect3 } from "react";
5382
+ import { createPortal as createPortal3 } from "react-dom";
5383
+ function injectTokens3(theme) {
5384
+ if (typeof document === "undefined") return;
5385
+ const resolved = theme === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : theme;
5386
+ let el = document.getElementById("decane-tokens");
5387
+ if (!el) {
5388
+ el = document.createElement("style");
5389
+ el.id = "decane-tokens";
5390
+ document.head.appendChild(el);
5391
+ }
5392
+ el.textContent = `:root { ${CSS_VARS[resolved]} }`;
5393
+ }
5394
+ function WalletStatusOverlay({
5395
+ phase,
5396
+ theme = "auto"
5397
+ }) {
5398
+ useEffect3(() => {
5399
+ if (!phase) return;
5400
+ injectTokens3(theme);
5401
+ if (theme !== "auto") return;
5402
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
5403
+ const h = () => injectTokens3("auto");
5404
+ mq.addEventListener("change", h);
5405
+ return () => mq.removeEventListener("change", h);
5406
+ }, [phase, theme]);
5407
+ if (!phase || typeof document === "undefined") return null;
5408
+ const title = phase === "creating" ? "Creating your wallet" : "Unlocking your wallet";
5409
+ const sub = phase === "creating" ? "Generating your keys and splitting them across three shares so no single party ever holds your wallet. This takes a few seconds \u2014 please keep this tab open." : "Reopening your secure session\u2026";
5410
+ return createPortal3(
5411
+ /* @__PURE__ */ React4.createElement(
5412
+ "div",
5413
+ {
5414
+ role: "dialog",
5415
+ "aria-modal": "true",
5416
+ "aria-label": title,
5417
+ style: {
5418
+ position: "fixed",
5419
+ inset: 0,
5420
+ zIndex: 1e4,
5421
+ display: "flex",
5422
+ alignItems: "center",
5423
+ justifyContent: "center",
5424
+ padding: 16,
5425
+ fontFamily: "var(--dc-font-sans, system-ui, sans-serif)"
5426
+ }
5427
+ },
5428
+ /* @__PURE__ */ React4.createElement("div", { style: {
5429
+ position: "absolute",
5430
+ inset: 0,
5431
+ background: "var(--dc-bg-overlay, rgba(0,0,0,0.75))",
5432
+ backdropFilter: "blur(8px)",
5433
+ WebkitBackdropFilter: "blur(8px)"
5434
+ } }),
5435
+ /* @__PURE__ */ React4.createElement("div", { style: {
5436
+ position: "relative",
5437
+ background: "var(--dc-bg, #0D0D0B)",
5438
+ border: "1px solid var(--dc-border, #3D3C39)",
5439
+ borderRadius: 20,
5440
+ width: "100%",
5441
+ maxWidth: 340,
5442
+ padding: "30px 26px",
5443
+ textAlign: "center",
5444
+ boxShadow: "0 24px 64px rgba(0,0,0,0.6)"
5445
+ } }, /* @__PURE__ */ React4.createElement("div", { style: { display: "flex", justifyContent: "center", marginBottom: 18 } }, /* @__PURE__ */ React4.createElement("div", { style: { position: "relative", width: 60, height: 60, display: "flex", alignItems: "center", justifyContent: "center" } }, /* @__PURE__ */ React4.createElement("span", { style: {
5446
+ position: "absolute",
5447
+ inset: 0,
5448
+ borderRadius: "50%",
5449
+ border: "2px solid var(--dc-border, #3D3C39)",
5450
+ borderTopColor: "var(--dc-accent, #F5C800)",
5451
+ animation: "dc-status-spin 0.8s linear infinite"
5452
+ } }), /* @__PURE__ */ React4.createElement(DecaneMark, { size: 32 }))), /* @__PURE__ */ React4.createElement("div", { style: { fontSize: 16, fontWeight: 800, color: "var(--dc-text, #F9F9F7)", letterSpacing: "-0.02em", marginBottom: 7 } }, title), /* @__PURE__ */ React4.createElement("div", { style: { fontSize: 12, color: "var(--dc-text-muted, #8A8884)", lineHeight: 1.55 } }, sub), /* @__PURE__ */ React4.createElement("div", { style: { marginTop: 20, paddingTop: 14, borderTop: "1px solid var(--dc-border, #3D3C39)", display: "flex", alignItems: "center", justifyContent: "center", gap: 6 } }, /* @__PURE__ */ React4.createElement(DecaneMark, { size: 13, muted: true }), /* @__PURE__ */ React4.createElement(DecaneWordmark, { size: 11, muted: true }))),
5453
+ /* @__PURE__ */ React4.createElement("style", null, `@keyframes dc-status-spin { to { transform: rotate(360deg); } }`)
5454
+ ),
5455
+ document.body
5456
+ );
5457
+ }
5458
+
5459
+ // src/ui/context.tsx
5312
5460
  var DEFAULT_SOCIAL = {
5313
5461
  addresses: null,
5314
5462
  isNewUser: false,
5315
5463
  isUnlocked: false,
5316
5464
  sessionExpiresAt: null,
5317
5465
  needsReconnect: false,
5318
- canUsePasskey: false
5466
+ canUsePasskey: false,
5467
+ phase: null
5319
5468
  };
5320
5469
  var DecaneContext = createContext(null);
5321
5470
  function hasSocial(mode) {
@@ -5352,7 +5501,7 @@ function DecaneKit({ config, children }) {
5352
5501
  const closeSelector = useCallback3(() => setSelectorOpen(false), []);
5353
5502
  const openSocialModal = useCallback3(() => setSocialModalOpen(true), []);
5354
5503
  const closeSocialModal = useCallback3(() => setSocialModalOpen(false), []);
5355
- useEffect3(() => {
5504
+ useEffect4(() => {
5356
5505
  if (!socialAuth.isUnlocked) return;
5357
5506
  const id = setInterval(() => {
5358
5507
  const sdk = socialRef.current;
@@ -5365,7 +5514,7 @@ function DecaneKit({ config, children }) {
5365
5514
  }, 1e4);
5366
5515
  return () => clearInterval(id);
5367
5516
  }, [socialAuth.isUnlocked]);
5368
- useEffect3(() => {
5517
+ useEffect4(() => {
5369
5518
  if (!hasSocial(mode) || !config?.social || socialInitialized.current) return;
5370
5519
  socialInitialized.current = true;
5371
5520
  const {
@@ -5393,6 +5542,9 @@ function DecaneKit({ config, children }) {
5393
5542
  () => setSocialAuth((p) => ({ ...p, isUnlocked: false, sessionExpiresAt: null }))
5394
5543
  );
5395
5544
  sdk.on("disconnected", () => setSocialAuth(DEFAULT_SOCIAL));
5545
+ sdk.on("wallet-creating", () => setSocialAuth((p) => ({ ...p, phase: "creating" })));
5546
+ sdk.on("wallet-unlocking", () => setSocialAuth((p) => ({ ...p, phase: "unlocking" })));
5547
+ sdk.on("connected", () => setSocialAuth((p) => ({ ...p, phase: null })));
5396
5548
  const saved = sdk.getAddresses();
5397
5549
  if (saved) {
5398
5550
  setSocialAuth((p) => ({ ...p, addresses: saved, isUnlocked: false, needsReconnect: true }));
@@ -5401,7 +5553,10 @@ function DecaneKit({ config, children }) {
5401
5553
  });
5402
5554
  const params = new URLSearchParams(window.location.search);
5403
5555
  if (params.has("decane_jwt") || params.has("decane_error")) {
5404
- sdk.connectWithGoogle().then(applyResult).catch(console.error);
5556
+ sdk.connectWithGoogle().then(applyResult).catch((e) => {
5557
+ setSocialAuth((p) => p.phase ? { ...p, phase: null } : p);
5558
+ console.error(e);
5559
+ });
5405
5560
  }
5406
5561
  }).catch(console.error);
5407
5562
  }, []);
@@ -5413,19 +5568,26 @@ function DecaneKit({ config, children }) {
5413
5568
  isUnlocked: sdk?.isUnlocked() ?? false,
5414
5569
  sessionExpiresAt: sdk?.sessionExpiresAt() ?? null,
5415
5570
  needsReconnect: false,
5416
- canUsePasskey: false
5571
+ canUsePasskey: false,
5572
+ phase: null
5417
5573
  });
5418
5574
  sdk?.canSignInWithPasskey().then((can) => setSocialAuth((p) => ({ ...p, canUsePasskey: can }))).catch(() => {
5419
5575
  });
5420
5576
  }
5577
+ const clearPhase = useCallback3(() => setSocialAuth((p) => p.phase ? { ...p, phase: null } : p), []);
5421
5578
  const signInWithPasskey = useCallback3(async () => {
5422
5579
  const sdk = socialRef.current;
5423
5580
  if (!sdk) throw new Error("Social sign-in not configured");
5424
- const result = await sdk.signInWithPasskey();
5425
- applyResult(result);
5426
- setSelectorOpen(false);
5427
- return result;
5428
- }, []);
5581
+ try {
5582
+ const result = await sdk.signInWithPasskey();
5583
+ applyResult(result);
5584
+ setSelectorOpen(false);
5585
+ return result;
5586
+ } catch (e) {
5587
+ clearPhase();
5588
+ throw e;
5589
+ }
5590
+ }, [clearPhase]);
5429
5591
  const connectWithGoogle = useCallback3(async () => {
5430
5592
  const sdk = socialRef.current;
5431
5593
  if (!sdk) throw new Error("Social sign-in not configured");
@@ -5439,17 +5601,27 @@ function DecaneKit({ config, children }) {
5439
5601
  const verifyEmailCode2 = useCallback3(async (email, code) => {
5440
5602
  const sdk = socialRef.current;
5441
5603
  if (!sdk) throw new Error("Social sign-in not configured");
5442
- const result = await sdk.verifyEmailCode(email, code);
5443
- applyResult(result);
5444
- setSelectorOpen(false);
5445
- }, []);
5604
+ try {
5605
+ const result = await sdk.verifyEmailCode(email, code);
5606
+ applyResult(result);
5607
+ setSelectorOpen(false);
5608
+ } catch (e) {
5609
+ clearPhase();
5610
+ throw e;
5611
+ }
5612
+ }, [clearPhase]);
5446
5613
  const connectWithToken = useCallback3(async (token, opts) => {
5447
5614
  const sdk = socialRef.current;
5448
5615
  if (!sdk) throw new Error("Social sign-in not configured");
5449
- const result = await sdk.connectWithToken(token, opts);
5450
- applyResult(result);
5451
- setSelectorOpen(false);
5452
- }, []);
5616
+ try {
5617
+ const result = await sdk.connectWithToken(token, opts);
5618
+ applyResult(result);
5619
+ setSelectorOpen(false);
5620
+ } catch (e) {
5621
+ clearPhase();
5622
+ throw e;
5623
+ }
5624
+ }, [clearPhase]);
5453
5625
  const customAuthConfig = config?.social?.customAuth;
5454
5626
  const connectWithCustomAuth = useCallback3(async () => {
5455
5627
  if (!customAuthConfig) throw new Error("Custom auth not configured");
@@ -5459,14 +5631,21 @@ function DecaneKit({ config, children }) {
5459
5631
  const socialUnlock = useCallback3(async () => {
5460
5632
  const sdk = socialRef.current;
5461
5633
  if (!sdk) return;
5462
- await sdk.unlock();
5463
- setSocialAuth((p) => ({
5464
- ...p,
5465
- isUnlocked: sdk.isUnlocked(),
5466
- sessionExpiresAt: sdk.sessionExpiresAt(),
5467
- needsReconnect: false
5468
- }));
5469
- }, []);
5634
+ setSocialAuth((p) => ({ ...p, phase: "unlocking" }));
5635
+ try {
5636
+ await sdk.unlock();
5637
+ setSocialAuth((p) => ({
5638
+ ...p,
5639
+ isUnlocked: sdk.isUnlocked(),
5640
+ sessionExpiresAt: sdk.sessionExpiresAt(),
5641
+ needsReconnect: false,
5642
+ phase: null
5643
+ }));
5644
+ } catch (e) {
5645
+ clearPhase();
5646
+ throw e;
5647
+ }
5648
+ }, [clearPhase]);
5470
5649
  const socialDisconnect = useCallback3(async () => {
5471
5650
  const sdk = socialRef.current;
5472
5651
  if (!sdk) return;
@@ -5535,7 +5714,7 @@ function DecaneKit({ config, children }) {
5535
5714
  []
5536
5715
  );
5537
5716
  const networks = deriveNetworks(mode, config?.networks);
5538
- return /* @__PURE__ */ React4.createElement(
5717
+ return /* @__PURE__ */ React5.createElement(
5539
5718
  DecaneContext.Provider,
5540
5719
  {
5541
5720
  value: {
@@ -5573,7 +5752,7 @@ function DecaneKit({ config, children }) {
5573
5752
  }
5574
5753
  },
5575
5754
  children,
5576
- /* @__PURE__ */ React4.createElement(
5755
+ /* @__PURE__ */ React5.createElement(
5577
5756
  WalletSelector,
5578
5757
  {
5579
5758
  provider: walletRef.current,
@@ -5594,7 +5773,7 @@ function DecaneKit({ config, children }) {
5594
5773
  onCustomAuthSignIn: customAuthConfig ? connectWithCustomAuth : void 0
5595
5774
  }
5596
5775
  ),
5597
- /* @__PURE__ */ React4.createElement(
5776
+ /* @__PURE__ */ React5.createElement(
5598
5777
  SocialWalletModal,
5599
5778
  {
5600
5779
  open: socialModalOpen,
@@ -5609,12 +5788,16 @@ function DecaneKit({ config, children }) {
5609
5788
  onEmailStart: connectWithEmail,
5610
5789
  onEmailVerify: verifyEmailCode2,
5611
5790
  authMethods: config?.social?.authMethods ?? ["google", "email"],
5791
+ canUsePasskey: socialAuth.canUsePasskey,
5792
+ passkeyAddress: socialAuth.addresses?.evm ?? socialAuth.addresses?.solana ?? null,
5793
+ onPasskeySignIn: signInWithPasskey,
5612
5794
  onRotateShares: socialRotateShares,
5613
5795
  onExportPortableBackup: socialExportPortableBackup,
5614
5796
  rpcUrls: config?.social?.rpcUrls,
5615
5797
  chains: config?.social?.chains
5616
5798
  }
5617
- )
5799
+ ),
5800
+ /* @__PURE__ */ React5.createElement(WalletStatusOverlay, { phase: socialAuth.phase, theme })
5618
5801
  );
5619
5802
  }
5620
5803
  function useDecaneContext() {
@@ -5628,7 +5811,7 @@ function useDecaneContext() {
5628
5811
  }
5629
5812
 
5630
5813
  // src/ui/hooks.ts
5631
- import { useState as useState4, useEffect as useEffect4, useCallback as useCallback4 } from "react";
5814
+ import { useState as useState4, useEffect as useEffect5, useCallback as useCallback4 } from "react";
5632
5815
  function useSocialAuth() {
5633
5816
  const {
5634
5817
  socialAuth,
@@ -5638,6 +5821,7 @@ function useSocialAuth() {
5638
5821
  connectWithToken,
5639
5822
  customAuthLabel,
5640
5823
  connectWithCustomAuth,
5824
+ signInWithPasskey: ctxSignInWithPasskey,
5641
5825
  socialUnlock,
5642
5826
  socialDisconnect
5643
5827
  } = useDecaneContext();
@@ -5706,6 +5890,15 @@ function useSocialAuth() {
5706
5890
  setCustomLoading(false);
5707
5891
  }
5708
5892
  }, [connectWithCustomAuth]);
5893
+ const signInWithPasskey = useCallback4(async () => {
5894
+ setError(null);
5895
+ try {
5896
+ await ctxSignInWithPasskey();
5897
+ } catch (e) {
5898
+ setError(e.message);
5899
+ throw e;
5900
+ }
5901
+ }, [ctxSignInWithPasskey]);
5709
5902
  const unlock = useCallback4(async () => {
5710
5903
  setError(null);
5711
5904
  try {
@@ -5730,6 +5923,11 @@ function useSocialAuth() {
5730
5923
  isNewUser: socialAuth.isNewUser,
5731
5924
  addresses: socialAuth.addresses,
5732
5925
  sessionExpiresAt: socialAuth.sessionExpiresAt,
5926
+ // True when this device can skip the full sign-in and resume with a passkey.
5927
+ canUsePasskey: socialAuth.canUsePasskey,
5928
+ // Long-running work in progress, for driving your own status UI.
5929
+ phase: socialAuth.phase,
5930
+ isCreatingWallet: socialAuth.phase === "creating",
5733
5931
  // True when addresses are known (from a previous visit) but there's no
5734
5932
  // live session — unlock() will always throw in this state (every
5735
5933
  // unlock requires a fresh JWT, and JWTs are never persisted). Route
@@ -5747,6 +5945,7 @@ function useSocialAuth() {
5747
5945
  confirmEmailCode,
5748
5946
  signInWithToken,
5749
5947
  signInWithCustomAuth: connectWithCustomAuth ? signInWithCustomAuth : void 0,
5948
+ signInWithPasskey,
5750
5949
  unlock,
5751
5950
  disconnect
5752
5951
  };
@@ -5860,6 +6059,10 @@ function useSocialWallet() {
5860
6059
  // unlock requires a fresh JWT, and JWTs are never persisted). Route
5861
6060
  // this into a real reconnect (signInWithGoogle / sendEmailCode), not unlock().
5862
6061
  needsReconnect: socialAuth.needsReconnect,
6062
+ // Long-running work in progress ('creating' | 'unlocking' | null), for
6063
+ // driving your own status UI in addition to the SDK's built-in overlay.
6064
+ phase: socialAuth.phase,
6065
+ isCreatingWallet: socialAuth.phase === "creating",
5863
6066
  // Modal
5864
6067
  modalOpen: socialModalOpen,
5865
6068
  openModal: openSocialModal,
@@ -5908,7 +6111,7 @@ function useSocialBalance(chain) {
5908
6111
  setLoading(false);
5909
6112
  }
5910
6113
  }, [socialGetBalance, chain, socialAuth.addresses]);
5911
- useEffect4(() => {
6114
+ useEffect5(() => {
5912
6115
  let cancelled = false;
5913
6116
  if (!socialAuth.addresses) {
5914
6117
  setBalance(null);
@@ -5940,7 +6143,7 @@ function toDecaneError(err) {
5940
6143
  function useDecane() {
5941
6144
  const { provider, theme, setTheme } = useDecaneContext();
5942
6145
  const [, forceUpdate] = useState4(0);
5943
- useEffect4(() => {
6146
+ useEffect5(() => {
5944
6147
  const refresh = () => forceUpdate((n) => n + 1);
5945
6148
  const events = [
5946
6149
  "connect",
@@ -5982,7 +6185,7 @@ function useChainSwitch() {
5982
6185
  const [, forceUpdate] = useState4(0);
5983
6186
  const [loading, setLoading] = useState4(false);
5984
6187
  const [error, setError] = useState4(null);
5985
- useEffect4(() => {
6188
+ useEffect5(() => {
5986
6189
  const refresh = () => forceUpdate((n) => n + 1);
5987
6190
  provider.on("connect", refresh);
5988
6191
  provider.on("disconnect", refresh);