caplib
Version:
Credentialless Authentication Protocol Library for Web Applications
809 lines (802 loc) • 33.8 kB
JavaScript
;
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