UNPKG

caplib

Version:

Credentialless Authentication Protocol Library for Web Applications

809 lines (802 loc) 33.8 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/vue/index.ts var vue_exports = {}; __export(vue_exports, { VueCapAuth: () => CapAuth_default }); module.exports = __toCommonJS(vue_exports); // src/react/components/CapAuth.tsx var import_react3 = require("react"); var import_qrcode = require("qrcode.react"); var import_framer_motion = require("framer-motion"); var import_react4 = require("@chakra-ui/react"); var import_icons = require("@chakra-ui/icons"); // src/react/components/OnboardingForm.tsx var import_react = require("react"); var import_react2 = require("@chakra-ui/react"); // src/react/assets/zerocat-logo.ts var import_meta = {}; var logoUrl = ""; try { logoUrl = new URL("./zerocat.png", import_meta.url).href; } catch (e) { try { if (typeof window !== "undefined") { logoUrl = "/zerocat.png"; } } catch (err) { console.warn("Failed to load Zerocat logo:", err); } } var zerocat_logo_default = logoUrl; // src/react/components/OnboardingForm.tsx var import_jsx_runtime = require("react/jsx-runtime"); function OnboardingForm({ walletAddress, onComplete, onBack, theme = { primary: "blue", secondary: "teal" } }) { const toast = (0, import_react2.useToast)(); const [loading, setLoading] = (0, import_react.useState)(false); const [accountType, setAccountType] = (0, import_react.useState)("human"); const [errors, setErrors] = (0, import_react.useState)({}); const [availableRoles, setAvailableRoles] = (0, import_react.useState)(["User"]); const [defaultRole, setDefaultRole] = (0, import_react.useState)("User"); const [details, setDetails] = (0, import_react.useState)({ first_name: "", last_name: "", entity_name: "", email: "", role: "User" }); (0, import_react.useEffect)(() => { const fetchRoles = async () => { try { const response = await fetch("/api/roles"); if (response.ok) { const data = await response.json(); if (data.roles && Array.isArray(data.roles) && data.roles.length > 0) { setAvailableRoles(data.roles); if (data.defaultRole) { setDefaultRole(data.defaultRole); setDetails((prev) => ({ ...prev, role: data.defaultRole })); } } } } catch (error) { console.error("Failed to fetch roles:", error); } }; fetchRoles(); }, []); const handleDetailsChange = (key, value) => { setDetails((prev) => ({ ...prev, [key]: value })); if (errors[key]) { setErrors((prev) => { const newErrors = { ...prev }; delete newErrors[key]; return newErrors; }); } }; const validateForm = () => { const newErrors = {}; if (accountType === "human") { if (!details.first_name.trim()) { newErrors.first_name = "First name is required"; } if (!details.last_name.trim()) { newErrors.last_name = "Last name is required"; } } else { if (!details.entity_name.trim()) { newErrors.entity_name = "Entity name is required"; } } if (!details.email.trim()) { newErrors.email = "Email is required"; } else if (!/\S+@\S+\.\S+/.test(details.email)) { newErrors.email = "Email is invalid"; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = async (e) => { e.preventDefault(); if (!validateForm()) { return; } setLoading(true); try { const payload = { wallet_address: walletAddress, first_name: accountType === "human" ? details.first_name : "", last_name: accountType === "human" ? details.last_name : "ENTITY", entity_name: accountType === "entity" ? details.entity_name : "", email: details.email, role: details.role, account_type: accountType }; const createResponse = await fetch("/api/users/create", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); if (!createResponse.ok) { throw new Error("Failed to create profile"); } const data = await createResponse.json(); onComplete(data.user); toast({ title: "Profile created", description: "Your profile has been successfully created.", status: "success", duration: 5e3, isClosable: true }); } catch (error) { console.error("Registration error:", error); toast({ title: "Registration failed", description: error instanceof Error ? error.message : "Failed to create account", status: "error", duration: 5e3, isClosable: true }); } finally { setLoading(false); } }; return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.Box, { width: "100%", p: 6, position: "relative", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.VStack, { spacing: 6, align: "stretch", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.Box, { textAlign: "center", mb: 6, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.Flex, { justifyContent: "center", mb: 4, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react2.Image, { src: zerocat_logo_default, alt: "ZEROCAT", height: "32px", width: "80px", objectFit: "contain" } ) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react2.Heading, { as: "h1", size: "xl", bgGradient: `linear(to-r, ${theme.primary}.400, ${theme.secondary}.400)`, backgroundClip: "text", mb: 2, children: "Complete Your Profile" } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.Text, { color: "whiteAlpha.700", children: "Tell us a bit about yourself" }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.VStack, { spacing: 6, align: "stretch", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.FormControl, { as: "fieldset", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.FormLabel, { as: "legend", fontWeight: "medium", children: "Account Type" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react2.RadioGroup, { defaultValue: "human", onChange: (value) => setAccountType(value), value: accountType, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.Stack, { direction: "row", spacing: 5, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.Radio, { value: "human", children: "Individual" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.Radio, { value: "entity", children: "Entity/Organization" }) ] }) } ) ] }), accountType === "human" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.Stack, { direction: { base: "column", md: "row" }, spacing: 4, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.FormControl, { isInvalid: !!errors.first_name, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.FormLabel, { children: "First Name" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react2.Input, { value: details.first_name, onChange: (e) => handleDetailsChange("first_name", e.target.value), placeholder: "John" } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.FormErrorMessage, { children: errors.first_name }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.FormControl, { isInvalid: !!errors.last_name, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.FormLabel, { children: "Last Name" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react2.Input, { value: details.last_name, onChange: (e) => handleDetailsChange("last_name", e.target.value), placeholder: "Doe" } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.FormErrorMessage, { children: errors.last_name }) ] }) ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.FormControl, { isInvalid: !!errors.entity_name, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.FormLabel, { children: "Organization Name" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react2.Input, { value: details.entity_name, onChange: (e) => handleDetailsChange("entity_name", e.target.value), placeholder: "Company or Organization Name" } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.FormErrorMessage, { children: errors.entity_name }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.FormControl, { isInvalid: !!errors.email, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.FormLabel, { children: "Email" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react2.Input, { type: "email", value: details.email, onChange: (e) => handleDetailsChange("email", e.target.value), placeholder: "you@example.com" } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.FormErrorMessage, { children: errors.email }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.FormControl, { children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.FormLabel, { children: "Role" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react2.Select, { value: details.role, onChange: (e) => handleDetailsChange("role", e.target.value), children: availableRoles.map((role) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: role, children: role }, role)) } ) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.Flex, { gap: 3, pt: 4, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react2.Button, { flex: "1", variant: "outline", onClick: onBack, isDisabled: loading, children: "Back" } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react2.Button, { flex: "1", variant: "gradient", type: "submit", isLoading: loading, loadingText: "Creating...", children: "Complete" } ) ] }) ] }) }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( import_react2.Flex, { justifyContent: "center", alignItems: "center", position: "absolute", bottom: "0", left: "0", right: "0", p: 2, borderTop: "1px", borderColor: "whiteAlpha.100", bg: "whiteAlpha.50", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.Text, { fontSize: "xs", color: "whiteAlpha.600", mr: 1, children: "Powered by" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react2.Image, { src: zerocat_logo_default, alt: "ZEROCAT", height: "12px", width: "28px" } ) ] } ) ] }); } // src/react/components/CapAuth.tsx var import_jsx_runtime2 = require("react/jsx-runtime"); var fadeIn = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { duration: 0.3 } }, exit: { opacity: 0, transition: { duration: 0.2 } } }; var slideRight = { hidden: { x: -30, opacity: 0 }, visible: { x: 0, opacity: 1, transition: { duration: 0.4 } }, exit: { x: 30, opacity: 0, transition: { duration: 0.3 } } }; var steps = [ { title: "Prepare" }, { title: "Scan" }, { title: "Verify" }, { title: "Profile" } ]; function CapAuth({ onAuthenticated, config, onError }) { const toast = (0, import_react4.useToast)(); const { activeStep, setActiveStep } = (0, import_react4.useSteps)({ index: 0, count: steps.length }); const [stage, setStage] = (0, import_react3.useState)("intro"); const [qrData, setQrData] = (0, import_react3.useState)(null); const [qrUri, setQrUri] = (0, import_react3.useState)(null); const [error, setError] = (0, import_react3.useState)(null); const [nonce, setNonce] = (0, import_react3.useState)(null); const [walletAddress, setWalletAddress] = (0, import_react3.useState)(null); const [isPollingActive, setIsPollingActive] = (0, import_react3.useState)(true); const pollingIntervalRef = (0, import_react3.useRef)(null); const timeoutIdRef = (0, import_react3.useRef)(null); const isAuthenticatedRef = (0, import_react3.useRef)(false); const cleanupTimers = () => { if (pollingIntervalRef.current) { clearInterval(pollingIntervalRef.current); pollingIntervalRef.current = null; } if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = null; } }; (0, import_react3.useEffect)(() => { return () => cleanupTimers(); }, []); (0, import_react3.useEffect)(() => { if (stage === "polling" && nonce && isPollingActive && !isAuthenticatedRef.current) { const interval = setInterval(async () => { try { const response = await fetch("/api/auth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ nonce }) }); if (!response.ok) throw new Error("Verification failed"); const data = await response.json(); if (data.authenticated && data.userAddress) { isAuthenticatedRef.current = true; setIsPollingActive(false); cleanupTimers(); if (data.user) { onAuthenticated(data.user); toast({ title: "Authentication successful", status: "success", duration: 3e3, isClosable: true }); } else { setActiveStep(3); setWalletAddress(data.userAddress); setStage("onboarding"); } } } catch (error2) { console.error("Polling error:", error2); } }, config.timeouts?.polling || 3e3); pollingIntervalRef.current = interval; const timeout = setTimeout(() => { cleanupTimers(); setError("Authentication timeout. Please try again."); setStage("intro"); setActiveStep(0); }, config.timeouts?.authentication || 3e5); timeoutIdRef.current = timeout; return () => cleanupTimers(); } }, [stage, nonce, isPollingActive, config, onAuthenticated, toast, setActiveStep]); const generateQR = async () => { try { setError(null); setActiveStep(1); const response = await fetch("/api/auth"); const data = await response.json(); if (!response.ok) { throw new Error(data.error || "Failed to generate authentication code"); } const receiverAddr = process.env.NEXT_PUBLIC_SERVER_WALLET_ADDRESS || ""; const contractId = process.env.NEXT_PUBLIC_AUTH_CONTRACT_ID || ""; const tokenId = process.env.NEXT_PUBLIC_TOKEN_ID || ""; const txDetails = `smart_contract_auth_${contractId},nonce_${data.nonce}`; const txParams = `amount=0,recipientAddress=${receiverAddr},senderAddress=customer_,timestamp=${(/* @__PURE__ */ new Date()).toISOString()},tokenId=${tokenId},transactionCost=0,transactionDetails=${txDetails},transactionId=transaction_`; setQrData(txParams); setQrUri(`zerowallet://transaction?${txParams}`); setNonce(data.nonce); setStage("qr"); } catch (error2) { console.error("QR generation error:", error2); setError("Failed to generate authentication code. Please try again."); toast({ title: "Error", description: "Failed to generate authentication code. Please try again.", status: "error", duration: 5e3, isClosable: true }); if (onError && error2 instanceof Error) { onError(error2); } } }; const handleOpenMobileWallet = () => { if (config.enableMobileWallet && qrUri) { window.location.href = qrUri; } }; const proceedToVerification = () => { setActiveStep(2); setStage("polling"); setIsPollingActive(true); }; const handleRetry = () => { cleanupTimers(); setError(null); setQrData(null); setQrUri(null); setNonce(null); setWalletAddress(null); setStage("intro"); setActiveStep(0); setIsPollingActive(true); isAuthenticatedRef.current = false; }; const handleOnboardingComplete = (user) => { setIsPollingActive(false); isAuthenticatedRef.current = true; if (walletAddress) { onAuthenticated(user); toast({ title: "Profile completed", description: "You're now successfully signed in.", status: "success", duration: 3e3, isClosable: true }); } }; const { theme = { primary: "blue", secondary: "teal" } } = config; return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( import_react4.Box, { width: { base: "90vw", sm: "100%" }, maxW: "md", borderRadius: "xl", overflow: "hidden", bg: "dark.200", borderWidth: "1px", borderColor: "whiteAlpha.200", boxShadow: "xl", position: "relative", children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Box, { p: { base: 2, sm: 4 }, bg: "whiteAlpha.50", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Stepper, { index: activeStep, colorScheme: "brand", size: "sm", children: steps.map((step, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.Step, { children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.StepIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_react4.StepStatus, { complete: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.StepIcon, {}), incomplete: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.StepNumber, {}), active: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.StepNumber, {}) } ) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Box, { flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.StepTitle, { fontSize: { base: "xs", sm: "sm" }, children: step.title }) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.StepSeparator, {}) ] }, index)) }) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_framer_motion.AnimatePresence, { mode: "wait", children: [ stage === "intro" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_framer_motion.motion.div, { variants: fadeIn, initial: "hidden", animate: "visible", exit: "exit", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.VStack, { spacing: { base: 4, sm: 6 }, p: { base: 4, sm: 6 }, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.Box, { textAlign: "center", children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_react4.Heading, { as: "h1", size: { base: "lg", sm: "xl" }, bgGradient: `linear(to-r, ${theme.primary}.400, ${theme.secondary}.400)`, backgroundClip: "text", mb: 2, children: config.appName } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Text, { color: "whiteAlpha.700", fontSize: { base: "sm", sm: "md" }, children: config.appDescription }) ] }), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.VStack, { spacing: 4, w: "full", children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( import_react4.Flex, { align: "flex-start", gap: 4, p: 4, bg: "whiteAlpha.50", borderRadius: "lg", w: "full", children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Box, { color: "blue.400", mt: 1, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.PhoneIcon, { boxSize: { base: 4, sm: 5 } }) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.Box, { children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Text, { fontWeight: "medium", fontSize: { base: "sm", sm: "md" }, children: "Get Your Wallet Ready" }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Text, { fontSize: { base: "xs", sm: "sm" }, color: "whiteAlpha.700", children: "Ensure you have ZeroWallet installed and ready" }) ] }) ] } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( import_react4.Flex, { align: "flex-start", gap: 4, p: 4, bg: "whiteAlpha.50", borderRadius: "lg", w: "full", children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Box, { color: "teal.400", mt: 1, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_react4.Icon, { viewBox: "0 0 24 24", boxSize: { base: 4, sm: 5 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "path", { fill: "currentColor", d: "M3 11h8V3H3v8zm2-6h4v4H5V5zm8-2v8h8V3h-8zm6 6h-4V5h4v4zM3 21h8v-8H3v8zm2-6h4v4H5v-4zm13-2h-2v4h-4v2h4v4h2v-4h4v-2h-4v-4z" } ) } ) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.Box, { children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Text, { fontWeight: "medium", fontSize: { base: "sm", sm: "md" }, children: "Credentialless Auth" }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Text, { fontSize: { base: "xs", sm: "sm" }, color: "whiteAlpha.700", children: "Scan QR code with your phone to open ZeroWallet" }) ] }) ] } ) ] }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_react4.Button, { w: "full", variant: "gradient", size: { base: "md", sm: "lg" }, onClick: generateQR, rightIcon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Icon, { viewBox: "0 0 24 24", boxSize: { base: 4, sm: 5 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "path", { fill: "currentColor", d: "M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8-8-8z" } ) }), children: "Start Authentication" } ) ] }) }, "intro" ), stage === "qr" && qrData && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_framer_motion.motion.div, { variants: slideRight, initial: "hidden", animate: "visible", exit: "exit", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.VStack, { spacing: { base: 4, sm: 6 }, p: { base: 4, sm: 6 }, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.Box, { textAlign: "center", children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Heading, { as: "h2", size: { base: "md", sm: "lg" }, mb: 2, children: "Scan QR Code" }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Text, { color: "whiteAlpha.700", fontSize: { base: "xs", sm: "sm" }, children: "Open your camera app and scan this code" }) ] }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Center, { py: { base: 4, sm: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Box, { p: { base: 3, sm: 4 }, bg: "white", borderRadius: "xl", boxShadow: "xl", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_qrcode.QRCodeSVG, { value: qrUri || "", size: Math.min(220, window.innerWidth * 0.6), level: "M", includeMargin: true } ) }) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.VStack, { spacing: 4, w: "full", children: [ config.enableMobileWallet && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_react4.Button, { w: "full", variant: "outline", onClick: handleOpenMobileWallet, leftIcon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.PhoneIcon, {}), size: { base: "sm", sm: "md" }, children: "Open in ZeroWallet" } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_react4.Button, { w: "full", variant: "gradient", onClick: proceedToVerification, rightIcon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.CheckIcon, {}), size: { base: "sm", sm: "md" }, children: "I've Completed the Transaction" } ) ] }) ] }) }, "qr" ), stage === "polling" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_framer_motion.motion.div, { variants: fadeIn, initial: "hidden", animate: "visible", exit: "exit", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.VStack, { spacing: 6, p: { base: 6, sm: 8 }, textAlign: "center", children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_react4.Spinner, { thickness: "4px", speed: "0.65s", emptyColor: "whiteAlpha.200", color: "brand.500", size: { base: "lg", sm: "xl" } } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.Box, { children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Heading, { as: "h2", size: { base: "md", sm: "lg" }, mb: 2, children: "Verifying" }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Text, { color: "whiteAlpha.700", fontSize: { base: "sm", sm: "md" }, children: "Please wait while we confirm your transaction..." }) ] }) ] }) }, "polling" ), stage === "onboarding" && walletAddress && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_framer_motion.motion.div, { variants: slideRight, initial: "hidden", animate: "visible", exit: "exit", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( OnboardingForm, { walletAddress, onComplete: handleOnboardingComplete, onBack: handleRetry, theme } ) }, "onboarding" ) ] }), error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_framer_motion.motion.div, { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Box, { p: { base: 3, sm: 4 }, bg: "red.900", color: "white", mt: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react4.VStack, { spacing: { base: 3, sm: 4 }, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Text, { color: "red.200", textAlign: "center", fontSize: { base: "sm", sm: "md" }, children: error }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_react4.Button, { leftIcon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.RepeatIcon, {}), colorScheme: "red", variant: "outline", size: "sm", onClick: handleRetry, children: "Try Again" } ) ] }) }) } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( import_react4.Flex, { justifyContent: "center", alignItems: "center", p: 2, borderTop: "1px", borderColor: "whiteAlpha.100", bg: "whiteAlpha.50", children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.Text, { fontSize: "xs", color: "whiteAlpha.600", mr: 1, children: "Powered by" }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_react4.Image, { src: zerocat_logo_default, alt: "ZEROCAT", height: { base: "10px", sm: "12px" }, width: { base: "24px", sm: "28px" } } ) ] } ) ] } ); } // src/vue/components/CapAuth.ts var import_client = require("react-dom/client"); var import_react5 = require("react"); function mountCapAuth(element, options) { const root = (0, import_client.createRoot)(element); root.render( (0, import_react5.createElement)(CapAuth, { config: options.config, onAuthenticated: options.onAuthenticated, onError: options.onError }) ); return () => { root.unmount(); }; } var CapAuth_default = { /** * Mount CapAuth into an element */ mount: mountCapAuth, /** * Vue directive (for use with v-capauth) */ directive: { mounted(el, binding) { const cleanup = mountCapAuth(el, binding.value); el.__capauth_cleanup = cleanup; }, unmounted(el) { const cleanup = el.__capauth_cleanup; if (typeof cleanup === "function") { cleanup(); } } } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { VueCapAuth }); //# sourceMappingURL=index.js.map