diff --git a/.changeset/social-dogs-bet.md b/.changeset/social-dogs-bet.md new file mode 100644 index 00000000000..af9849d7ed7 --- /dev/null +++ b/.changeset/social-dogs-bet.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Fix showQrModal option not respected on desktop web diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 35ac85fda9d..942fb1de5b0 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -57,7 +57,7 @@ "posthog-js": "1.256.1", "posthog-node": "^5.4.0", "prettier": "3.6.2", - "qrcode": "^1.5.3", + "qrcode": "1.5.3", "react": "19.1.0", "react-children-utilities": "^2.10.0", "react-day-picker": "^8.10.1", @@ -99,7 +99,7 @@ "@types/node": "22.14.1", "@types/papaparse": "^5.3.16", "@types/pluralize": "^0.0.33", - "@types/qrcode": "^1.5.5", + "@types/qrcode": "1.5.5", "@types/react": "19.1.8", "@types/react-dom": "19.1.6", "@types/react-table": "^7.7.20", diff --git a/apps/playground-web/src/app/wallets/sign-in/headless/page.tsx b/apps/playground-web/src/app/wallets/sign-in/headless/page.tsx index 771869c3525..c3302eb2334 100644 --- a/apps/playground-web/src/app/wallets/sign-in/headless/page.tsx +++ b/apps/playground-web/src/app/wallets/sign-in/headless/page.tsx @@ -71,13 +71,13 @@ function BuildCustomUISection() { { // 500+ wallets supported with id autocomplete const wallet = createWallet("io.metamask"); - await wallet.connect({ client }); + const isInstalled = !!injectedProvider("io.metamask"); + await wallet.connect({ client, ...(isInstalled ? {} : { + walletConnect: { + // if not installed, show qr modal + showQrModal: true, + onCancel: () => { + cancelConnection(); + } + } + }) + }); return wallet; }) } diff --git a/apps/playground-web/src/components/sign-in/hooks.tsx b/apps/playground-web/src/components/sign-in/hooks.tsx index d42a7f1abc7..e47f890d2eb 100644 --- a/apps/playground-web/src/components/sign-in/hooks.tsx +++ b/apps/playground-web/src/components/sign-in/hooks.tsx @@ -6,7 +6,7 @@ import { useDisconnect, } from "thirdweb/react"; import { shortenAddress } from "thirdweb/utils"; -import { createWallet } from "thirdweb/wallets"; +import { createWallet, injectedProvider } from "thirdweb/wallets"; import { THIRDWEB_CLIENT } from "../../lib/client"; import { Button } from "../ui/button"; @@ -21,6 +21,17 @@ export function HooksPreview() { const adminWallet = createWallet("io.metamask"); await adminWallet.connect({ client: THIRDWEB_CLIENT, + ...(injectedProvider("io.metamask") + ? {} + : { + walletConnect: { + showQrModal: true, + onCancel: () => { + console.log("onCancel"); + connectMutation.cancelConnection(); + }, + }, + }), }); return adminWallet; }); diff --git a/apps/playground-web/src/components/sign-in/modal.tsx b/apps/playground-web/src/components/sign-in/modal.tsx index a8bbc5a717a..d45b45a128d 100644 --- a/apps/playground-web/src/components/sign-in/modal.tsx +++ b/apps/playground-web/src/components/sign-in/modal.tsx @@ -54,7 +54,6 @@ export function ModalPreview({ enableAuth }: { enableAuth?: boolean }) { auth: enableAuth ? playgroundAuth : undefined, client: THIRDWEB_CLIENT, }); - console.log("connected", wallet); return wallet; }; diff --git a/apps/portal/src/components/Document/AuthMethodsTabs.tsx b/apps/portal/src/components/Document/AuthMethodsTabs.tsx index e3047317b38..c8c4446e5a1 100644 --- a/apps/portal/src/components/Document/AuthMethodsTabs.tsx +++ b/apps/portal/src/components/Document/AuthMethodsTabs.tsx @@ -3,7 +3,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import type React from "react"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import { getSocialIcon } from "thirdweb/wallets/in-app"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { @@ -819,7 +819,26 @@ const getUnrealSnippet = (authMethod: AuthMethod): string => { function AuthMethodsTabsContent() { const [selectedAuth, setSelectedAuth] = useState("email"); - const platforms = getPlatformsForAuth(selectedAuth); + const [selectedPlatform, setSelectedPlatform] = useState( + null, + ); + const platforms = useMemo( + () => getPlatformsForAuth(selectedAuth), + [selectedAuth], + ); + + // Reset platform selection when platforms change + const currentPlatform = useMemo(() => { + const defaultPlatform = platforms[0]?.id || "typescript"; + // If currently selected platform is not available in new platforms, reset to first available + if ( + !selectedPlatform || + !platforms.find((p) => p.id === selectedPlatform) + ) { + return defaultPlatform; + } + return selectedPlatform; + }, [platforms, selectedPlatform]); return (
@@ -858,7 +877,10 @@ function AuthMethodsTabsContent() {

2. Select Platform

- + setSelectedPlatform(value as Platform)} + > {platforms.map((platform) => { const IconComponent = platform.icon; diff --git a/packages/thirdweb/package.json b/packages/thirdweb/package.json index 88fbbebe124..8988aa682f4 100644 --- a/packages/thirdweb/package.json +++ b/packages/thirdweb/package.json @@ -36,6 +36,7 @@ "ora": "8.2.0", "ox": "0.7.0", "prompts": "2.4.2", + "qrcode": "1.5.3", "toml": "3.0.0", "uqr": "0.1.2", "viem": "2.28.1", @@ -61,6 +62,7 @@ "@testing-library/user-event": "^14.6.1", "@types/cross-spawn": "^6.0.6", "@types/prompts": "2.4.9", + "@types/qrcode": "1.5.5", "@types/react": "19.1.8", "@viem/anvil": "0.0.10", "@vitejs/plugin-react": "^4.6.0", diff --git a/packages/thirdweb/src/react/core/hooks/wallets/useConnect.ts b/packages/thirdweb/src/react/core/hooks/wallets/useConnect.ts index 726fead15f6..24651bd2efc 100644 --- a/packages/thirdweb/src/react/core/hooks/wallets/useConnect.ts +++ b/packages/thirdweb/src/react/core/hooks/wallets/useConnect.ts @@ -79,5 +79,15 @@ export function useConnect(options?: ConnectManagerOptions) { [connect, options, setConnectionStatus], ); - return { connect: handleConnection, error, isConnecting } as const; + const cancelConnection = useCallback(() => { + setIsConnecting(false); + setConnectionStatus("disconnected"); + }, [setConnectionStatus]); + + return { + connect: handleConnection, + error, + isConnecting, + cancelConnection, + } as const; } diff --git a/packages/thirdweb/src/wallets/create-wallet.ts b/packages/thirdweb/src/wallets/create-wallet.ts index 95e7994497a..6e8eb76567e 100644 --- a/packages/thirdweb/src/wallets/create-wallet.ts +++ b/packages/thirdweb/src/wallets/create-wallet.ts @@ -17,6 +17,7 @@ import { inAppWallet } from "./in-app/web/in-app.js"; import { getInjectedProvider } from "./injected/index.js"; import type { Account, Wallet } from "./interfaces/wallet.js"; import { smartWallet } from "./smart/smart-wallet.js"; +import type { QROverlay } from "./wallet-connect/qr-overlay.js"; import type { WCConnectOptions } from "./wallet-connect/types.js"; import { createWalletEmitter } from "./wallet-emitter.js"; import type { @@ -299,30 +300,90 @@ export function createWallet( "./wallet-connect/controller.js" ); - const [ - connectedAccount, - connectedChain, - doDisconnect, - doSwitchChain, - ] = await connectWC( - wcOptions, - emitter, - wallet.id as WCSupportedWalletIds | "walletConnect", - webLocalStorage, - sessionHandler, - ); - // set the states - account = connectedAccount; - chain = connectedChain; - handleDisconnect = doDisconnect; - handleSwitchChain = doSwitchChain; - trackConnect({ - chainId: chain.id, - client: wcOptions.client, - walletAddress: account.address, - walletType: id, - }); - return account; + let qrOverlay: QROverlay | undefined; + + try { + const [ + connectedAccount, + connectedChain, + doDisconnect, + doSwitchChain, + ] = await connectWC( + { + ...wcOptions, + walletConnect: { + ...wcOptions.walletConnect, + onDisplayUri: wcOptions.walletConnect?.showQrModal + ? async (uri) => { + // Check if we're in a browser environment + if ( + typeof window !== "undefined" && + typeof document !== "undefined" + ) { + try { + const { createQROverlay } = await import( + "./wallet-connect/qr-overlay.js" + ); + + // Clean up any existing overlay + if (qrOverlay) { + qrOverlay.destroy(); + } + + // Create new QR overlay + qrOverlay = createQROverlay(uri, { + theme: + wcOptions.walletConnect?.qrModalOptions + ?.themeMode ?? "dark", + qrSize: 280, + showCloseButton: true, + onCancel: () => { + wcOptions.walletConnect?.onCancel?.(); + }, + }); + } catch (error) { + console.error( + "Failed to create QR overlay:", + error, + ); + } + } + } + : undefined, + }, + }, + emitter, + wallet.id as WCSupportedWalletIds | "walletConnect", + webLocalStorage, + sessionHandler, + ); + + // Clean up QR overlay on successful connection + if (qrOverlay) { + qrOverlay.destroy(); + qrOverlay = undefined; + } + + // set the states + account = connectedAccount; + chain = connectedChain; + handleDisconnect = doDisconnect; + handleSwitchChain = doSwitchChain; + trackConnect({ + chainId: chain.id, + client: wcOptions.client, + walletAddress: account.address, + walletType: id, + }); + return account; + } catch (error) { + // Clean up QR overlay on connection error + if (qrOverlay) { + qrOverlay.destroy(); + qrOverlay = undefined; + } + throw error; + } } if (id === "walletConnect") { diff --git a/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts b/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts new file mode 100644 index 00000000000..24a665fe6d0 --- /dev/null +++ b/packages/thirdweb/src/wallets/wallet-connect/qr-overlay.ts @@ -0,0 +1,322 @@ +/** + * Vanilla JavaScript QR Code overlay utility for WalletConnect URIs + * Works in any browser context without requiring React or other frameworks + */ + +interface QROverlayOptions { + /** + * Custom styles to apply to the overlay + */ + overlayStyles?: Partial; + /** + * Custom styles to apply to the modal container + */ + modalStyles?: Partial; + /** + * QR code size in pixels + */ + qrSize?: number; + /** + * Show close button + */ + showCloseButton?: boolean; + /** + * Custom close button text + */ + closeButtonText?: string; + /** + * Theme preference + */ + theme?: "light" | "dark"; + /** + * Custom container element to append the overlay to + */ + container?: HTMLElement; + /** + * Callback called when user cancels the overlay (closes without connecting) + */ + onCancel?: () => void; +} + +export interface QROverlay { + /** + * Remove the overlay from the DOM + */ + destroy: () => void; + /** + * Hide the overlay (without removing from DOM) + */ + hide: () => void; + /** + * Show the overlay + */ + show: () => void; +} + +/** + * Creates a QR code overlay for the given WalletConnect URI + */ +export function createQROverlay( + uri: string, + options: QROverlayOptions = {}, +): QROverlay { + const { + qrSize = 280, + showCloseButton = true, + closeButtonText = "×", + theme = "light", + container = document.body, + onCancel, + } = options; + + // Create overlay backdrop + const overlay = document.createElement("div"); + overlay.style.cssText = ` + position: fixed; + inset: 0; + background-color: ${theme === "dark" ? "rgba(0, 0, 0, 0.8)" : "rgba(0, 0, 0, 0.5)"}; + backdrop-filter: blur(10px); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + animation: fadeIn 300ms ease-out; + `; + + // Apply custom overlay styles + if (options.overlayStyles) { + Object.assign(overlay.style, options.overlayStyles); + } + + // Create modal container + const modal = document.createElement("div"); + modal.style.cssText = ` + background: ${theme === "dark" ? "#1f1f1f" : "#ffffff"}; + border-radius: 16px; + padding: 24px; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + max-width: 90vw; + max-height: 90vh; + position: relative; + animation: scaleIn 300ms ease-out; + `; + + // Apply custom modal styles + if (options.modalStyles) { + Object.assign(modal.style, options.modalStyles); + } + + // Create close button + if (showCloseButton) { + const closeButton = document.createElement("button"); + closeButton.textContent = closeButtonText; + closeButton.style.cssText = ` + position: absolute; + top: 16px; + right: 16px; + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: ${theme === "dark" ? "#ffffff" : "#000000"}; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 8px; + transition: background-color 0.2s; + `; + + closeButton.addEventListener("mouseenter", () => { + closeButton.style.backgroundColor = + theme === "dark" ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.1)"; + }); + + closeButton.addEventListener("mouseleave", () => { + closeButton.style.backgroundColor = "transparent"; + }); + + closeButton.addEventListener("click", () => { + destroyOverlay(true); + }); + + modal.appendChild(closeButton); + } + + // Create QR code container + const qrContainer = document.createElement("div"); + qrContainer.style.cssText = ` + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + `; + + // Create title + const title = document.createElement("h3"); + title.textContent = "Scan to Connect"; + title.style.cssText = ` + margin: 0; + font-size: 18px; + font-weight: 600; + color: ${theme === "dark" ? "#ffffff" : "#000000"}; + text-align: center; + `; + + // Create QR code canvas + const qrCanvas = document.createElement("canvas"); + qrCanvas.width = qrSize; + qrCanvas.height = qrSize; + qrCanvas.style.cssText = ` + border: 1px solid ${theme === "dark" ? "#333333" : "#e5e5e5"}; + border-radius: 12px; + `; + + // Generate QR code + generateQRCode(uri, qrCanvas, qrSize).catch(console.error); + + // Create copy button for the URI + const copyButton = document.createElement("button"); + copyButton.textContent = "Copy URI"; + copyButton.style.cssText = ` + background: ${theme === "dark" ? "#333333" : "#f5f5f5"}; + border: 1px solid ${theme === "dark" ? "#444444" : "#e5e5e5"}; + color: ${theme === "dark" ? "#ffffff" : "#000000"}; + padding: 8px 16px; + border-radius: 8px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s; + `; + + copyButton.addEventListener("click", async () => { + try { + await navigator.clipboard.writeText(uri); + const originalText = copyButton.textContent; + copyButton.textContent = "Copied!"; + setTimeout(() => { + copyButton.textContent = originalText; + }, 2000); + } catch (err) { + console.error("Failed to copy URI:", err); + } + }); + + // Assemble the modal + qrContainer.appendChild(title); + qrContainer.appendChild(qrCanvas); + qrContainer.appendChild(copyButton); + modal.appendChild(qrContainer); + overlay.appendChild(modal); + + // Add CSS animations + const style = document.createElement("style"); + style.textContent = ` + @keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } + } + @keyframes fadeOut { + from { opacity: 1; } + to { opacity: 0; } + } + @keyframes scaleIn { + from { transform: scale(0.9); opacity: 0; } + to { transform: scale(1); opacity: 1; } + } + @keyframes scaleOut { + from { transform: scale(1); opacity: 1; } + to { transform: scale(0.9); opacity: 0; } + } + `; + document.head.appendChild(style); + + // Event handlers + const handleEscapeKey = (event: KeyboardEvent) => { + if (event.key === "Escape") { + destroyOverlay(true); + } + }; + + const handleOverlayClick = (event: MouseEvent) => { + if (event.target === overlay) { + destroyOverlay(true); + } + }; + + // Add event listeners + document.addEventListener("keydown", handleEscapeKey); + overlay.addEventListener("click", handleOverlayClick); + + // Append to container + container.appendChild(overlay); + + function destroyOverlay(userInitiated = false) { + document.removeEventListener("keydown", handleEscapeKey); + overlay.removeEventListener("click", handleOverlayClick); + + // Call onCancel callback only if user initiated the close action + if (userInitiated && onCancel) { + onCancel(); + } + + // Animate both overlay and modal out + overlay.style.animation = "fadeOut 200ms ease-in"; + modal.style.animation = "scaleOut 200ms ease-in"; + + const cleanup = () => { + if (overlay.parentNode) { + overlay.parentNode.removeChild(overlay); + } + if (style.parentNode) { + style.parentNode.removeChild(style); + } + }; + + overlay.addEventListener("animationend", cleanup, { once: true }); + + // Fallback cleanup in case animation doesn't fire + setTimeout(cleanup, 250); + } + + function hideOverlay() { + overlay.style.display = "none"; + } + + function showOverlay() { + overlay.style.display = "flex"; + } + + return { + destroy: () => destroyOverlay(false), // Programmatic cleanup, don't call onCancel + hide: hideOverlay, + show: showOverlay, + }; +} + +/** + * QR code generator that tries to use a real QR library if available, + * otherwise falls back to a placeholder + */ +async function generateQRCode( + text: string, + canvas: HTMLCanvasElement, + size: number, +) { + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + // Try to dynamically import a QR code library if available + // This allows the overlay to work with or without a QR code dependency + const { toCanvas } = await import("qrcode"); + await toCanvas(canvas, text, { + width: size, + margin: 2, + color: { + dark: "#000000", + light: "#ffffff", + }, + }); + return; +} diff --git a/packages/thirdweb/src/wallets/wallet-connect/types.ts b/packages/thirdweb/src/wallets/wallet-connect/types.ts index 826927a87b2..1061ee790cf 100644 --- a/packages/thirdweb/src/wallets/wallet-connect/types.ts +++ b/packages/thirdweb/src/wallets/wallet-connect/types.ts @@ -157,6 +157,19 @@ export type WCConnectOptions = { * ``` */ onDisplayUri?: (_uri: string) => void; + /** + * Callback that gets called when the user cancels the connection process (e.g., closes the QR modal). + * This is only called for user-initiated cancellations, not for programmatic cleanup. + * + * ```tsx + * await wallet.connect({ + * onCancel: () => { + * console.log("User cancelled wallet connection"); + * } + * }) + * ``` + */ + onCancel?: () => void; } >; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1674410f02a..95c33f8ad0e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,7 +135,7 @@ importers: version: 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@scalar/api-reference-react': specifier: 0.7.25 - version: 0.7.25(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.4)(react@19.1.0)(tailwindcss@3.4.17)(typescript@5.8.3) + version: 0.7.25(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.3)(react@19.1.0)(tailwindcss@3.4.17)(typescript@5.8.3) '@sentry/nextjs': specifier: 9.34.0 version: 9.34.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.5(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.99.9(esbuild@0.25.5)) @@ -233,8 +233,8 @@ importers: specifier: 3.6.2 version: 3.6.2 qrcode: - specifier: ^1.5.3 - version: 1.5.4 + specifier: 1.5.3 + version: 1.5.3 react: specifier: 19.1.0 version: 19.1.0 @@ -354,7 +354,7 @@ importers: specifier: ^0.0.33 version: 0.0.33 '@types/qrcode': - specifier: ^1.5.5 + specifier: 1.5.5 version: 1.5.5 '@types/react': specifier: 19.1.8 @@ -1314,6 +1314,9 @@ importers: prompts: specifier: 2.4.2 version: 2.4.2 + qrcode: + specifier: 1.5.3 + version: 1.5.3 toml: specifier: 3.0.0 version: 3.0.0 @@ -1384,6 +1387,9 @@ importers: '@types/prompts': specifier: 2.4.9 version: 2.4.9 + '@types/qrcode': + specifier: 1.5.5 + version: 1.5.5 '@types/react': specifier: 19.1.8 version: 19.1.8 @@ -13287,11 +13293,6 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - qrcode@1.5.4: - resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} - engines: {node: '>=10.13.0'} - hasBin: true - qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} @@ -20879,7 +20880,7 @@ snapshots: mipd: 0.0.7(typescript@5.8.3) ofetch: 1.4.1 pino-pretty: 10.3.1 - qrcode: 1.5.4 + qrcode: 1.5.3 react: 19.1.0 react-device-detect: 2.2.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-dom: 19.1.0(react@19.1.0) @@ -22460,7 +22461,7 @@ snapshots: '@safe-global/safe-gateway-typescript-sdk@3.23.1': {} - '@scalar/api-client@2.5.13(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.4)(tailwindcss@3.4.17)(typescript@5.8.3)': + '@scalar/api-client@2.5.13(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.3)(tailwindcss@3.4.17)(typescript@5.8.3)': dependencies: '@headlessui/tailwindcss': 0.2.2(tailwindcss@3.4.17) '@headlessui/vue': 1.7.23(vue@3.5.13(typescript@5.8.3)) @@ -22483,7 +22484,7 @@ snapshots: '@scalar/use-tooltip': 1.1.0(typescript@5.8.3) '@types/har-format': 1.2.16 '@vueuse/core': 10.11.1(vue@3.5.13(typescript@5.8.3)) - '@vueuse/integrations': 11.3.0(axios@1.10.0)(focus-trap@7.6.4)(fuse.js@7.1.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.4)(vue@3.5.13(typescript@5.8.3)) + '@vueuse/integrations': 11.3.0(axios@1.10.0)(focus-trap@7.6.4)(fuse.js@7.1.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.3)(vue@3.5.13(typescript@5.8.3)) focus-trap: 7.6.4 fuse.js: 7.1.0 microdiff: 1.5.0 @@ -22513,9 +22514,9 @@ snapshots: - typescript - universal-cookie - '@scalar/api-reference-react@0.7.25(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.4)(react@19.1.0)(tailwindcss@3.4.17)(typescript@5.8.3)': + '@scalar/api-reference-react@0.7.25(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.3)(react@19.1.0)(tailwindcss@3.4.17)(typescript@5.8.3)': dependencies: - '@scalar/api-reference': 1.32.1(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.4)(tailwindcss@3.4.17)(typescript@5.8.3) + '@scalar/api-reference': 1.32.1(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.3)(tailwindcss@3.4.17)(typescript@5.8.3) '@scalar/types': 0.2.5 react: 19.1.0 transitivePeerDependencies: @@ -22534,11 +22535,11 @@ snapshots: - typescript - universal-cookie - '@scalar/api-reference@1.32.1(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.4)(tailwindcss@3.4.17)(typescript@5.8.3)': + '@scalar/api-reference@1.32.1(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.3)(tailwindcss@3.4.17)(typescript@5.8.3)': dependencies: '@floating-ui/vue': 1.1.6(vue@3.5.13(typescript@5.8.3)) '@headlessui/vue': 1.7.23(vue@3.5.13(typescript@5.8.3)) - '@scalar/api-client': 2.5.13(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.4)(tailwindcss@3.4.17)(typescript@5.8.3) + '@scalar/api-client': 2.5.13(axios@1.10.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.3)(tailwindcss@3.4.17)(typescript@5.8.3) '@scalar/code-highlight': 0.1.4 '@scalar/components': 0.14.14(typescript@5.8.3) '@scalar/helpers': 0.0.5 @@ -25385,7 +25386,7 @@ snapshots: - '@vue/composition-api' - vue - '@vueuse/integrations@11.3.0(axios@1.10.0)(focus-trap@7.6.4)(fuse.js@7.1.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.4)(vue@3.5.13(typescript@5.8.3))': + '@vueuse/integrations@11.3.0(axios@1.10.0)(focus-trap@7.6.4)(fuse.js@7.1.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(qrcode@1.5.3)(vue@3.5.13(typescript@5.8.3))': dependencies: '@vueuse/core': 11.3.0(vue@3.5.13(typescript@5.8.3)) '@vueuse/shared': 11.3.0(vue@3.5.13(typescript@5.8.3)) @@ -25396,7 +25397,7 @@ snapshots: fuse.js: 7.1.0 idb-keyval: 6.2.2 nprogress: 0.2.0 - qrcode: 1.5.4 + qrcode: 1.5.3 transitivePeerDependencies: - '@vue/composition-api' - vue @@ -33732,12 +33733,6 @@ snapshots: pngjs: 5.0.0 yargs: 15.4.1 - qrcode@1.5.4: - dependencies: - dijkstrajs: 1.0.3 - pngjs: 5.0.0 - yargs: 15.4.1 - qs@6.14.0: dependencies: side-channel: 1.1.0