@magic-ext/magic-widget 1.0.0-canary.979.21008432451.0 → 1.0.0-canary.979.21048249937.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,3 @@
1
1
  import React from 'react';
2
- export declare function MagicWidget(): React.JSX.Element;
2
+ import { MagicWidgetProps } from './types';
3
+ export declare function MagicWidget({ displayMode, isOpen, onClose, closeOnSuccess, closeOnClickOutside, wallets, onSuccess, onError, }: MagicWidgetProps): React.JSX.Element | null;
@@ -1,3 +1,3 @@
1
- import { OAuthProvider, ProviderMetadata, ThirdPartyWallets } from './types';
2
- export declare const WALLET_METADATA: Record<ThirdPartyWallets, ProviderMetadata>;
1
+ import { OAuthProvider, ProviderMetadata, ThirdPartyWallet } from './types';
2
+ export declare const WALLET_METADATA: Record<ThirdPartyWallet, ProviderMetadata>;
3
3
  export declare const OAUTH_METADATA: Record<OAuthProvider, ProviderMetadata>;
@@ -0,0 +1,22 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { LoginResult, MagicWidgetProps, ThirdPartyWallet } from '../types';
3
+ interface WidgetConfigContextValue {
4
+ /** Third-party wallets to display */
5
+ wallets: ThirdPartyWallet[];
6
+ /** Call when login succeeds */
7
+ handleSuccess: (result: LoginResult) => void;
8
+ /** Call when login fails */
9
+ handleError: (error: Error) => void;
10
+ /** Call to close the widget. Undefined if onClose prop wasn't provided. */
11
+ handleClose?: () => void;
12
+ }
13
+ interface WidgetConfigProviderProps extends MagicWidgetProps {
14
+ children: ReactNode;
15
+ }
16
+ export declare function WidgetConfigProvider({ children, wallets, onSuccess, onError, onClose, closeOnSuccess, }: WidgetConfigProviderProps): React.JSX.Element;
17
+ /**
18
+ * Hook to access the widget configuration
19
+ * @throws Error if used outside of WidgetConfigProvider
20
+ */
21
+ export declare function useWidgetConfig(): WidgetConfigContextValue;
22
+ export {};
@@ -1 +1,2 @@
1
1
  export { EmailLoginProvider, useEmailLogin } from './EmailLoginContext';
2
+ export { WidgetConfigProvider, useWidgetConfig } from './WidgetConfigContext';
@@ -3,6 +3,6 @@ export interface UseSiweLoginResult {
3
3
  isLoading: boolean;
4
4
  error: Error | null;
5
5
  isSuccess: boolean;
6
- publicAddress: string | null;
6
+ walletAddress: string | null;
7
7
  }
8
8
  export declare function useSiweLogin(): UseSiweLoginResult;
@@ -1,4 +1,4 @@
1
- import { ThirdPartyWallets } from '../types';
1
+ import { ThirdPartyWallet } from '../types';
2
2
  export interface UseWalletConnectResult {
3
3
  connectWallet: () => Promise<void>;
4
4
  isPending: boolean;
@@ -9,4 +9,4 @@ export interface UseWalletConnectResult {
9
9
  isConnectedToSelectedProvider: boolean;
10
10
  disconnect: () => void;
11
11
  }
12
- export declare function useWalletConnect(provider: ThirdPartyWallets): UseWalletConnectResult;
12
+ export declare function useWalletConnect(provider: ThirdPartyWallet): UseWalletConnectResult;
@@ -1,2 +1,3 @@
1
1
  export * from './MagicWidget';
2
2
  export * from './extension';
3
+ export { ThirdPartyWallets, OAuthProvider, type ThirdPartyWallet, type DisplayMode, type MagicWidgetProps, type LoginResult, type EmailLoginResult, type OAuthLoginResult, type WalletLoginResult, type OAuthUserInfo, } from './types';
@@ -1,4 +1,4 @@
1
- import { LoginProvider, OAuthProvider, ThirdPartyWallets } from './types';
1
+ import { LoginProvider, OAuthProvider, ThirdPartyWallet } from './types';
2
2
  export type View = 'login' | 'otp' | 'additional_providers' | 'wallet_pending' | 'oauth_pending' | 'email_otp_pending' | 'device_verification' | 'mfa_pending' | 'recovery_code' | 'lost_recovery_code' | 'login_success';
3
3
  export type EmailLoginStatus = 'idle' | 'sending' | 'otp_sent' | 'verifying_otp' | 'invalid_otp' | 'expired_otp' | 'max_attempts_reached' | 'device_needs_approval' | 'device_verification_sent' | 'device_verification_expired' | 'device_approved' | 'mfa_required' | 'recovery_code' | 'success' | 'mfa_verifying' | 'mfa_invalid' | 'recovery_code_verifying' | 'lost_recovery_code' | 'error';
4
4
  export interface WidgetState {
@@ -59,7 +59,7 @@ export type WidgetAction = {
59
59
  type: 'GO_TO_ADDITIONAL_PROVIDERS';
60
60
  } | {
61
61
  type: 'SELECT_WALLET';
62
- provider: ThirdPartyWallets;
62
+ provider: ThirdPartyWallet;
63
63
  };
64
64
  export declare const initialState: WidgetState;
65
65
  export declare function widgetReducer(state: WidgetState, action: WidgetAction): WidgetState;
@@ -1,11 +1,17 @@
1
1
  import { ComponentType } from 'react';
2
- export declare enum ThirdPartyWallets {
3
- METAMASK = "metamask",
4
- WALLETCONNECT = "walletconnect",
5
- COINBASE = "coinbase",
6
- PHANTOM = "phantom",
7
- RABBY = "rabby"
8
- }
2
+ /**
3
+ * Available third-party wallet providers.
4
+ * Use these constants or pass string literals directly: 'metamask', 'coinbase', etc.
5
+ */
6
+ export declare const ThirdPartyWallets: {
7
+ readonly METAMASK: "metamask";
8
+ readonly WALLETCONNECT: "walletconnect";
9
+ readonly COINBASE: "coinbase";
10
+ readonly PHANTOM: "phantom";
11
+ readonly RABBY: "rabby";
12
+ };
13
+ /** Type representing valid third-party wallet values */
14
+ export type ThirdPartyWallet = (typeof ThirdPartyWallets)[keyof typeof ThirdPartyWallets];
9
15
  export interface ProviderMetadata {
10
16
  displayName: string;
11
17
  Icon: ComponentType<{
@@ -32,7 +38,7 @@ export declare enum OAuthProvider {
32
38
  GITLAB = "gitlab",
33
39
  TELEGRAM = "telegram"
34
40
  }
35
- export type LoginProvider = OAuthProvider | ThirdPartyWallets;
41
+ export type LoginProvider = OAuthProvider | ThirdPartyWallet;
36
42
  export interface ProviderConfig {
37
43
  title: string;
38
44
  description: string;
@@ -42,3 +48,114 @@ export interface ProviderConfig {
42
48
  className?: string;
43
49
  }>;
44
50
  }
51
+ /**
52
+ * OAuth user information returned from social login
53
+ */
54
+ export interface OAuthUserInfo {
55
+ /** The OAuth provider used (e.g., 'google', 'apple') */
56
+ provider: string;
57
+ name?: string;
58
+ email?: string;
59
+ picture?: string;
60
+ [key: string]: unknown;
61
+ }
62
+ /**
63
+ * Result returned on successful email login
64
+ */
65
+ export interface EmailLoginResult {
66
+ method: 'email';
67
+ /** The DID token for authentication */
68
+ didToken: string;
69
+ }
70
+ /**
71
+ * Result returned on successful OAuth login
72
+ */
73
+ export interface OAuthLoginResult {
74
+ method: 'oauth';
75
+ /** The DID token for authentication */
76
+ didToken: string;
77
+ /** OAuth provider information */
78
+ oauth: OAuthUserInfo;
79
+ }
80
+ /**
81
+ * Result returned on successful wallet login
82
+ */
83
+ export interface WalletLoginResult {
84
+ method: 'wallet';
85
+ /** The connected wallet address */
86
+ walletAddress: string;
87
+ }
88
+ /**
89
+ * Discriminated union of all login result types.
90
+ * Check the `method` property to determine which fields are available.
91
+ *
92
+ * @example
93
+ * onSuccess={(result) => {
94
+ * if (result.method === 'email' || result.method === 'oauth') {
95
+ * sendToBackend(result.didToken);
96
+ * } else {
97
+ * console.log('Wallet:', result.walletAddress);
98
+ * }
99
+ * }}
100
+ */
101
+ export type LoginResult = EmailLoginResult | OAuthLoginResult | WalletLoginResult;
102
+ /**
103
+ * How the widget is displayed on the page.
104
+ * - 'inline': Rendered in the document flow (default)
105
+ * - 'modal': Positioned as a modal dialog, slightly above center
106
+ */
107
+ export type DisplayMode = 'inline' | 'modal';
108
+ export interface MagicWidgetProps {
109
+ /**
110
+ * How the widget is displayed on the page. Defaults to 'inline'.
111
+ * - 'inline': Rendered in the document flow
112
+ * - 'modal': Positioned as a modal dialog with backdrop
113
+ * @example displayMode="modal"
114
+ */
115
+ displayMode?: DisplayMode;
116
+ /**
117
+ * Whether the widget modal is open. Defaults to true.
118
+ * @example isOpen={showLogin}
119
+ */
120
+ isOpen?: boolean;
121
+ /**
122
+ * Callback fired when the widget is closed (e.g., user clicks X button).
123
+ * @example onClose={() => setShowLogin(false)}
124
+ */
125
+ onClose?: () => void;
126
+ /**
127
+ * Automatically call onClose after successful login (with a 2 second delay to show success screen).
128
+ * Defaults to false.
129
+ * @example closeOnSuccess={true}
130
+ */
131
+ closeOnSuccess?: boolean;
132
+ /**
133
+ * Close the modal when clicking the backdrop. Only applies when displayMode="modal".
134
+ * Defaults to false.
135
+ * @example closeOnClickOutside={true}
136
+ */
137
+ closeOnClickOutside?: boolean;
138
+ /**
139
+ * Third-party wallets to display. None enabled by default.
140
+ * Accepts constants or string literals: 'metamask', 'walletconnect', 'coinbase', 'phantom', 'rabby'
141
+ * @example wallets={['metamask', 'coinbase']}
142
+ * @example wallets={[ThirdPartyWallets.METAMASK, ThirdPartyWallets.COINBASE]}
143
+ */
144
+ wallets?: ThirdPartyWallet[];
145
+ /**
146
+ * Callback fired when login succeeds.
147
+ * The result type varies by login method - check `result.method` to determine the shape.
148
+ * @example
149
+ * onSuccess={(result) => {
150
+ * if (result.method === 'email' || result.method === 'oauth') {
151
+ * authenticate(result.didToken);
152
+ * }
153
+ * }}
154
+ */
155
+ onSuccess?: (result: LoginResult) => void;
156
+ /**
157
+ * Callback fired when login fails
158
+ * @example onError={(error) => console.error(error.message)}
159
+ */
160
+ onError?: (error: Error) => void;
161
+ }
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
2
  import { WidgetAction } from '../reducer';
3
- import { ThirdPartyWallets } from '../types';
3
+ import { ThirdPartyWallet } from '../types';
4
4
  interface WalletPendingViewProps {
5
- provider: ThirdPartyWallets;
5
+ provider: ThirdPartyWallet;
6
6
  dispatch: React.Dispatch<WidgetAction>;
7
7
  }
8
8
  export declare const WalletPendingView: ({ provider, dispatch }: WalletPendingViewProps) => React.JSX.Element;
@@ -1,9 +1,9 @@
1
- import { ThirdPartyWallets } from '../types';
1
+ import { ThirdPartyWallet } from '../types';
2
2
  /**
3
- * Map ThirdPartyWallets enum to wagmi connector IDs
3
+ * Map wallet types to wagmi connector IDs
4
4
  */
5
- export declare const CONNECTOR_IDS: Record<ThirdPartyWallets, string>;
5
+ export declare const CONNECTOR_IDS: Record<ThirdPartyWallet, string>;
6
6
  /**
7
7
  * Alternative names to match connectors by name if ID doesn't match
8
8
  */
9
- export declare const CONNECTOR_NAME_PATTERNS: Record<ThirdPartyWallets, string>;
9
+ export declare const CONNECTOR_NAME_PATTERNS: Record<ThirdPartyWallet, string>;
package/dist/es/index.mjs CHANGED
@@ -13943,14 +13943,17 @@ const ProviderButton = ({ label, Icon, onPress, hideLabel }) => {
13943
13943
  !hideLabel && label && (React__default.createElement(M$8, { fontWeight: "medium", styles: { lineHeight: '1.5rem' } }, label)))));
13944
13944
  };
13945
13945
 
13946
- var ThirdPartyWallets;
13947
- (function (ThirdPartyWallets) {
13948
- ThirdPartyWallets["METAMASK"] = "metamask";
13949
- ThirdPartyWallets["WALLETCONNECT"] = "walletconnect";
13950
- ThirdPartyWallets["COINBASE"] = "coinbase";
13951
- ThirdPartyWallets["PHANTOM"] = "phantom";
13952
- ThirdPartyWallets["RABBY"] = "rabby";
13953
- })(ThirdPartyWallets || (ThirdPartyWallets = {}));
13946
+ /**
13947
+ * Available third-party wallet providers.
13948
+ * Use these constants or pass string literals directly: 'metamask', 'coinbase', etc.
13949
+ */
13950
+ const ThirdPartyWallets = {
13951
+ METAMASK: 'metamask',
13952
+ WALLETCONNECT: 'walletconnect',
13953
+ COINBASE: 'coinbase',
13954
+ PHANTOM: 'phantom',
13955
+ RABBY: 'rabby',
13956
+ };
13954
13957
  var RpcErrorMessage;
13955
13958
  (function (RpcErrorMessage) {
13956
13959
  RpcErrorMessage["MalformedEmail"] = "Invalid params: Please provide a valid email address.";
@@ -14355,8 +14358,48 @@ class MagicWidgetExtension extends Extension.Internal {
14355
14358
  }
14356
14359
  }
14357
14360
 
14361
+ const WidgetConfigContext = createContext(null);
14362
+ function WidgetConfigProvider({ children, wallets = [], onSuccess, onError, onClose, closeOnSuccess = false, }) {
14363
+ const handleSuccess = useCallback((result) => {
14364
+ onSuccess?.(result);
14365
+ if (closeOnSuccess && onClose) {
14366
+ // Delay closing so users can see the success screen
14367
+ setTimeout(() => {
14368
+ onClose();
14369
+ }, 2000);
14370
+ }
14371
+ }, [onSuccess, closeOnSuccess, onClose]);
14372
+ const handleError = useCallback((error) => {
14373
+ onError?.(error);
14374
+ }, [onError]);
14375
+ const handleClose = onClose
14376
+ ? () => {
14377
+ onClose();
14378
+ }
14379
+ : undefined;
14380
+ const value = {
14381
+ wallets,
14382
+ handleSuccess,
14383
+ handleError,
14384
+ handleClose,
14385
+ };
14386
+ return React__default.createElement(WidgetConfigContext.Provider, { value: value }, children);
14387
+ }
14388
+ /**
14389
+ * Hook to access the widget configuration
14390
+ * @throws Error if used outside of WidgetConfigProvider
14391
+ */
14392
+ function useWidgetConfig() {
14393
+ const context = useContext(WidgetConfigContext);
14394
+ if (!context) {
14395
+ throw new Error('useWidgetConfig must be used within a WidgetConfigProvider');
14396
+ }
14397
+ return context;
14398
+ }
14399
+
14358
14400
  const EmailLoginContext = createContext(null);
14359
14401
  function EmailLoginProvider({ children, dispatch }) {
14402
+ const { handleSuccess, handleError } = useWidgetConfig();
14360
14403
  // Store the current login handle
14361
14404
  const handleRef = useRef(null);
14362
14405
  const emailRef = useRef(null);
@@ -14435,19 +14478,24 @@ function EmailLoginProvider({ children, dispatch }) {
14435
14478
  .then(didToken => {
14436
14479
  if (didToken) {
14437
14480
  dispatch({ type: 'LOGIN_SUCCESS' });
14481
+ handleSuccess({ method: 'email', didToken });
14438
14482
  }
14439
14483
  })
14440
14484
  .catch(error => {
14441
- dispatch({ type: 'LOGIN_ERROR', error: error?.message || 'Login failed' });
14485
+ const errorInstance = error instanceof Error ? error : new Error(error?.message || 'Login failed');
14486
+ dispatch({ type: 'LOGIN_ERROR', error: errorInstance.message });
14487
+ handleError(errorInstance);
14442
14488
  });
14443
14489
  }
14444
14490
  catch (error) {
14491
+ const errorInstance = error instanceof Error ? error : new Error('Failed to start login');
14445
14492
  dispatch({
14446
14493
  type: 'LOGIN_ERROR',
14447
- error: error instanceof Error ? error.message : 'Failed to start login',
14494
+ error: errorInstance.message,
14448
14495
  });
14496
+ handleError(errorInstance);
14449
14497
  }
14450
- }, [dispatch]);
14498
+ }, [dispatch, handleSuccess, handleError]);
14451
14499
  /**
14452
14500
  * Submit OTP code for verification
14453
14501
  */
@@ -14596,10 +14644,7 @@ const SocialProviders = ({ providers, onPress, dispatch }) => {
14596
14644
  };
14597
14645
 
14598
14646
  function WidgetHeader({ showHeaderText = true, onPressBack }) {
14599
- const handleClose = () => {
14600
- // TODO: Need to determine what this behavior should be/if it should exist at all
14601
- console.log('Close widget');
14602
- };
14647
+ const { handleClose } = useWidgetConfig();
14603
14648
  return (React__default.createElement(u$3, { position: "relative" },
14604
14649
  !!onPressBack && (React__default.createElement(u$3.LeftAction, null,
14605
14650
  React__default.createElement(Q$2, { size: "sm", variant: "neutral", onPress: onPressBack },
@@ -14607,19 +14652,19 @@ function WidgetHeader({ showHeaderText = true, onPressBack }) {
14607
14652
  React__default.createElement(s$6, null))))),
14608
14653
  showHeaderText && (React__default.createElement(u$3.Content, null,
14609
14654
  React__default.createElement(M$8, { size: "sm", fontColor: "text.tertiary" }, "Log in or sign up"))),
14610
- React__default.createElement(u$3.RightAction, null,
14655
+ handleClose && (React__default.createElement(u$3.RightAction, null,
14611
14656
  React__default.createElement(Q$2, { size: "sm", variant: "neutral", onPress: handleClose },
14612
14657
  React__default.createElement(Q$2.TrailingIcon, null,
14613
- React__default.createElement(c$a, null))))));
14658
+ React__default.createElement(c$a, null)))))));
14614
14659
  }
14615
14660
 
14616
14661
  const LoginView = ({ dispatch }) => {
14617
14662
  const config = getExtensionInstance().getConfig();
14663
+ const { wallets } = useWidgetConfig();
14618
14664
  const { primary, social } = config?.authProviders ?? {};
14619
14665
  const hasEmailProvider = primary?.includes('email');
14620
14666
  const socialProviders = social?.map(provider => provider) ?? [];
14621
- const enabledWalletProviders = Object.values(ThirdPartyWallets).filter(provider => provider !== ThirdPartyWallets.WALLETCONNECT);
14622
- const showDivider = socialProviders.length > 0 && enabledWalletProviders.length > 0;
14667
+ const showDivider = (hasEmailProvider || socialProviders.length > 0) && wallets.length > 0;
14623
14668
  const handleProviderSelect = (provider) => {
14624
14669
  dispatch({ type: 'SELECT_WALLET', provider });
14625
14670
  };
@@ -14637,7 +14682,7 @@ const LoginView = ({ dispatch }) => {
14637
14682
  React__default.createElement(Divider, { color: "surface.quaternary" }),
14638
14683
  React__default.createElement(M$8, { "aria-label": "or", fontColor: "text.tertiary" }, "or"),
14639
14684
  React__default.createElement(Divider, { color: "surface.quaternary" }))),
14640
- enabledWalletProviders.length > 0 && (React__default.createElement(HStack, { gap: 2, w: "full" }, enabledWalletProviders.map(provider => (React__default.createElement(ProviderButton, { key: provider, hideLabel: enabledWalletProviders.length > 1, label: WALLET_METADATA[provider].displayName, Icon: WALLET_METADATA[provider].Icon, onPress: () => handleProviderSelect(provider) })))))))));
14685
+ wallets.length > 0 && (React__default.createElement(Flex, { gap: 2, w: "full", direction: showDivider ? 'row' : 'column', justify: "center" }, wallets.map(provider => (React__default.createElement(ProviderButton, { key: provider, hideLabel: wallets.length > 1 && showDivider, label: WALLET_METADATA[provider].displayName, Icon: WALLET_METADATA[provider].Icon, onPress: () => handleProviderSelect(provider) })))))))));
14641
14686
  };
14642
14687
 
14643
14688
  function getProviderConfig(provider) {
@@ -14659,7 +14704,7 @@ function getProviderConfig(provider) {
14659
14704
  }
14660
14705
 
14661
14706
  /**
14662
- * Map ThirdPartyWallets enum to wagmi connector IDs
14707
+ * Map wallet types to wagmi connector IDs
14663
14708
  */
14664
14709
  const CONNECTOR_IDS = {
14665
14710
  [ThirdPartyWallets.METAMASK]: 'metaMaskSDK',
@@ -14747,15 +14792,16 @@ function useWalletConnect(provider) {
14747
14792
  function useSiweLogin() {
14748
14793
  const { signMessageAsync } = useSignMessage();
14749
14794
  const connectedChainId = useChainId();
14795
+ const { handleSuccess, handleError } = useWidgetConfig();
14750
14796
  const [isLoading, setIsLoading] = useState(false);
14751
14797
  const [error, setError] = useState(null);
14752
14798
  const [isSuccess, setIsSuccess] = useState(false);
14753
- const [publicAddress, setPublicAddress] = useState(null);
14799
+ const [walletAddress, setWalletAddress] = useState(null);
14754
14800
  const performSiweLogin = useCallback(async (address, chainId) => {
14755
14801
  setIsLoading(true);
14756
14802
  setError(null);
14757
14803
  setIsSuccess(false);
14758
- setPublicAddress(null);
14804
+ setWalletAddress(null);
14759
14805
  try {
14760
14806
  const extension = getExtensionInstance();
14761
14807
  const effectiveChainId = chainId || connectedChainId || 1;
@@ -14767,28 +14813,30 @@ function useSiweLogin() {
14767
14813
  // Step 2: Sign the message with the connected wallet
14768
14814
  const signature = await signMessageAsync({ message });
14769
14815
  // Step 3: Send the signed message to Magic backend for verification
14770
- const publicAddress = await extension.login({ message, signature });
14816
+ await extension.login({ message, signature });
14771
14817
  // Step 4: Set up the connected state for 3rd party wallet RPC routing
14772
14818
  // This enables signing requests to be routed through the connected wallet
14773
14819
  extension.setConnectedState(address, effectiveChainId);
14774
14820
  setIsSuccess(true);
14775
- setPublicAddress(publicAddress);
14821
+ setWalletAddress(address);
14776
14822
  setIsLoading(false);
14777
- return publicAddress;
14823
+ handleSuccess({ method: 'wallet', walletAddress: address });
14824
+ return address;
14778
14825
  }
14779
14826
  catch (err) {
14780
14827
  const errorInstance = err instanceof Error ? err : new Error('SIWE login failed');
14781
14828
  setError(errorInstance);
14782
14829
  setIsLoading(false);
14830
+ handleError(errorInstance);
14783
14831
  throw errorInstance;
14784
14832
  }
14785
- }, [signMessageAsync, connectedChainId]);
14833
+ }, [signMessageAsync, connectedChainId, handleSuccess, handleError]);
14786
14834
  return {
14787
14835
  performSiweLogin,
14788
14836
  isLoading,
14789
14837
  error,
14790
14838
  isSuccess,
14791
- publicAddress,
14839
+ walletAddress,
14792
14840
  };
14793
14841
  }
14794
14842
 
@@ -14800,7 +14848,7 @@ const centeredIconClass = css({
14800
14848
  });
14801
14849
  const Pending = ({ onPressBack, title, description, Icon, isPending, errorMessage }) => {
14802
14850
  return (React__default.createElement(React__default.Fragment, null,
14803
- React__default.createElement(WidgetHeader, { onPressBack: onPressBack, showHeaderText: false }),
14851
+ React__default.createElement(WidgetHeader, { onPressBack: isPending ? onPressBack : undefined, showHeaderText: false }),
14804
14852
  React__default.createElement(VStack, { gap: 6, pt: 4 },
14805
14853
  React__default.createElement(Box, { position: "relative", h: 20, w: 20 },
14806
14854
  isPending && React__default.createElement(d$m, { size: 80, strokeWidth: 8, neutral: true, progress: 40 }),
@@ -15043,6 +15091,7 @@ function widgetReducer(state, action) {
15043
15091
  }
15044
15092
 
15045
15093
  function useOAuthLogin() {
15094
+ const { handleSuccess, handleError } = useWidgetConfig();
15046
15095
  const [isLoading, setIsLoading] = useState(false);
15047
15096
  const [error, setError] = useState(null);
15048
15097
  const [isSuccess, setIsSuccess] = useState(false);
@@ -15058,15 +15107,26 @@ function useOAuthLogin() {
15058
15107
  setIsSuccess(true);
15059
15108
  setResult(oauthResult);
15060
15109
  setIsLoading(false);
15110
+ handleSuccess({
15111
+ method: 'oauth',
15112
+ didToken: oauthResult.magic.idToken,
15113
+ oauth: {
15114
+ provider: oauthResult.oauth.provider,
15115
+ name: oauthResult.oauth.userInfo.name,
15116
+ email: oauthResult.oauth.userInfo.email,
15117
+ picture: oauthResult.oauth.userInfo.picture,
15118
+ },
15119
+ });
15061
15120
  return oauthResult;
15062
15121
  }
15063
15122
  catch (err) {
15064
15123
  const errorInstance = err instanceof Error ? err : new Error('OAuth login failed');
15065
15124
  setError(errorInstance);
15066
15125
  setIsLoading(false);
15126
+ handleError(errorInstance);
15067
15127
  throw errorInstance;
15068
15128
  }
15069
- }, []);
15129
+ }, [handleSuccess, handleError]);
15070
15130
  return {
15071
15131
  performOAuthLogin,
15072
15132
  isLoading,
@@ -15351,30 +15411,82 @@ function WidgetContent({ state, dispatch }) {
15351
15411
  renderView(),
15352
15412
  React__default.createElement(c$5, null)))));
15353
15413
  }
15354
- // Main widget component - no props needed, everything is internal
15355
- function MagicWidget() {
15414
+ // Styles for modal mode
15415
+ const modalBackdropStyles = {
15416
+ position: 'fixed',
15417
+ top: 0,
15418
+ left: 0,
15419
+ right: 0,
15420
+ bottom: 0,
15421
+ backdropFilter: 'blur(0.375rem)',
15422
+ WebkitBackdropFilter: 'blur(0.375rem)', // Safari support
15423
+ display: 'flex',
15424
+ alignItems: 'flex-start',
15425
+ justifyContent: 'center',
15426
+ paddingTop: '15vh', // Slightly above center
15427
+ zIndex: 9999,
15428
+ };
15429
+ const modalContentStyles = {
15430
+ position: 'relative',
15431
+ };
15432
+ // Main widget component
15433
+ function MagicWidget({ displayMode = 'inline', isOpen = true, onClose, closeOnSuccess = false, closeOnClickOutside = false, wallets = [], onSuccess, onError, }) {
15356
15434
  const [state, dispatch] = useReducer(widgetReducer, initialState);
15357
- const [isConfigLoading, setIsConfigLoading] = useState(true);
15435
+ // Check if config is already cached to avoid unnecessary loading state
15436
+ const [isConfigLoading, setIsConfigLoading] = useState(() => {
15437
+ return getExtensionInstance().getConfig() === null;
15438
+ });
15358
15439
  useEffect(() => {
15359
15440
  injectCSS();
15360
- getExtensionInstance()
15361
- .fetchConfig()
15362
- .then(() => setIsConfigLoading(false))
15363
- .catch(err => {
15364
- console.error('Failed to fetch config:', err);
15365
- setIsConfigLoading(false); // Still show widget on error
15366
- });
15367
- }, []);
15441
+ // Only fetch if not already cached
15442
+ if (isConfigLoading) {
15443
+ getExtensionInstance()
15444
+ .fetchConfig()
15445
+ .then(() => setIsConfigLoading(false))
15446
+ .catch(err => {
15447
+ console.error('Failed to fetch config:', err);
15448
+ setIsConfigLoading(false); // Still show widget on error
15449
+ });
15450
+ }
15451
+ }, [isConfigLoading]);
15452
+ // Reset to login view when modal is opened
15453
+ useEffect(() => {
15454
+ if (isOpen) {
15455
+ dispatch({ type: 'GO_TO_LOGIN' });
15456
+ }
15457
+ }, [isOpen]);
15458
+ if (!isOpen) {
15459
+ return null;
15460
+ }
15461
+ const isModal = displayMode === 'modal';
15462
+ // Handle backdrop click for closeOnClickOutside
15463
+ const handleBackdropClick = (e) => {
15464
+ // Only close if clicking the backdrop itself, not the content
15465
+ if (closeOnClickOutside && e.target === e.currentTarget && onClose) {
15466
+ onClose();
15467
+ }
15468
+ };
15368
15469
  if (isConfigLoading) {
15369
- return (React__default.createElement(r$3, null,
15470
+ const loadingContent = (React__default.createElement(r$3, null,
15370
15471
  React__default.createElement(VStack, { alignItems: "center", justifyContent: "center", height: "300px" },
15371
15472
  React__default.createElement(d$m, null))));
15473
+ if (isModal) {
15474
+ return (React__default.createElement("div", { style: modalBackdropStyles, onClick: handleBackdropClick },
15475
+ React__default.createElement("div", { style: modalContentStyles }, loadingContent)));
15476
+ }
15477
+ return loadingContent;
15478
+ }
15479
+ const widgetContent = (React__default.createElement(WidgetConfigProvider, { wallets: wallets, onSuccess: onSuccess, onError: onError, onClose: onClose, closeOnSuccess: closeOnSuccess },
15480
+ React__default.createElement(WagmiProvider, { config: wagmiConfig },
15481
+ React__default.createElement(QueryClientProvider, { client: queryClient },
15482
+ React__default.createElement("div", { id: "magic-widget-container" },
15483
+ React__default.createElement(WidgetContent, { state: state, dispatch: dispatch }))))));
15484
+ if (isModal) {
15485
+ return (React__default.createElement("div", { style: modalBackdropStyles, onClick: handleBackdropClick },
15486
+ React__default.createElement("div", { style: modalContentStyles }, widgetContent)));
15372
15487
  }
15373
- return (React__default.createElement(WagmiProvider, { config: wagmiConfig },
15374
- React__default.createElement(QueryClientProvider, { client: queryClient },
15375
- React__default.createElement("div", { id: "magic-widget-container" },
15376
- React__default.createElement(WidgetContent, { state: state, dispatch: dispatch })))));
15488
+ return widgetContent;
15377
15489
  }
15378
15490
 
15379
- export { MAGIC_WIDGET_PROVIDER, MagicWidget, MagicWidgetExtension, getExtensionInstance };
15491
+ export { MAGIC_WIDGET_PROVIDER, MagicWidget, MagicWidgetExtension, OAuthProvider, ThirdPartyWallets, getExtensionInstance };
15380
15492
  //# sourceMappingURL=index.mjs.map