diff --git a/cdrm-frontend/.prettierrc.json b/cdrm-frontend/.prettierrc.json index eb0d496..13040da 100644 --- a/cdrm-frontend/.prettierrc.json +++ b/cdrm-frontend/.prettierrc.json @@ -4,5 +4,6 @@ "semi": true, "singleQuote": false, "useTabs": false, - "printWidth": 100 + "printWidth": 100, + "plugins": ["prettier-plugin-tailwindcss"] } diff --git a/cdrm-frontend/package-lock.json b/cdrm-frontend/package-lock.json index 5d0ec6d..caeddcf 100644 --- a/cdrm-frontend/package-lock.json +++ b/cdrm-frontend/package-lock.json @@ -12,8 +12,10 @@ "axios": "^1.10.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-icons": "^5.5.0", "react-router-dom": "^7.7.0", "shaka-player": "^4.15.8", + "sonner": "^2.0.6", "tailwindcss": "^4.1.11" }, "devDependencies": { @@ -22,10 +24,12 @@ "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.7.0", "@vitejs/plugin-react-swc": "^3.11.0", + "daisyui": "^5.0.46", "eslint": "^9.31.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", + "prettier-plugin-tailwindcss": "^0.6.14", "vite": "^7.0.5" } }, @@ -2137,6 +2141,16 @@ "dev": true, "license": "MIT" }, + "node_modules/daisyui": { + "version": "5.0.46", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.0.46.tgz", + "integrity": "sha512-vMDZK1tI/bOb2Mc3Mk5WpquBG3ZqBz1YKZ0xDlvpOvey60dOS4/5Qhdowq1HndbQl7PgDLDYysxAjjUjwR7/eQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/saadeghi/daisyui?sponsor=1" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -3479,6 +3493,110 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.6.14", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz", + "integrity": "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-hermes": "*", + "@prettier/plugin-oxc": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-multiline-arrays": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-hermes": { + "optional": true + }, + "@prettier/plugin-oxc": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "@zackad/prettier-plugin-twig": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-multiline-arrays": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -3516,6 +3634,15 @@ "react": "^19.1.0" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -3667,6 +3794,16 @@ "node": ">=8" } }, + "node_modules/sonner": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.6.tgz", + "integrity": "sha512-yHFhk8T/DK3YxjFQXIrcHT1rGEeTLliVzWbO0xN8GberVun2RiBnxAjXAYpZrqwEVHBG9asI/Li8TAAhN9m59Q==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", diff --git a/cdrm-frontend/package.json b/cdrm-frontend/package.json index fd98c46..515869a 100644 --- a/cdrm-frontend/package.json +++ b/cdrm-frontend/package.json @@ -14,8 +14,10 @@ "axios": "^1.10.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-icons": "^5.5.0", "react-router-dom": "^7.7.0", "shaka-player": "^4.15.8", + "sonner": "^2.0.6", "tailwindcss": "^4.1.11" }, "devDependencies": { @@ -24,10 +26,12 @@ "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.7.0", "@vitejs/plugin-react-swc": "^3.11.0", + "daisyui": "^5.0.46", "eslint": "^9.31.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", + "prettier-plugin-tailwindcss": "^0.6.14", "vite": "^7.0.5" } } diff --git a/cdrm-frontend/src/App.jsx b/cdrm-frontend/src/App.jsx index ed2c71b..1921f7e 100644 --- a/cdrm-frontend/src/App.jsx +++ b/cdrm-frontend/src/App.jsx @@ -1,48 +1,19 @@ -import { useState } from "react"; -import Home from "./components/Pages/HomePage"; -import Cache from "./components/Pages/Cache"; -import API from "./components/Pages/API"; -import TestPlayer from "./components/Pages/TestPlayer"; -import NavBar from "./components/NavBar"; -import NavBarMain from "./components/NavBarMain"; -import SideMenu from "./components/SideMenu"; // Add this import +import { Route, Routes } from "react-router-dom"; import Account from "./components/Pages/Account"; -import { Routes, Route } from "react-router-dom"; +import API from "./components/Pages/API"; +import Cache from "./components/Pages/Cache"; +import Home from "./components/Pages/HomePage"; +import TestPlayer from "./components/Pages/TestPlayer"; function App() { - const [isMenuOpen, setIsMenuOpen] = useState(false); // Track if the menu is open - return ( -
- {/* The SideMenu should be visible when isMenuOpen is true */} - - - - -
- - -
- - } /> - } /> - } /> - } /> - } /> - -
-
-
+ + } /> + } /> + } /> + } /> + } /> + ); } diff --git a/cdrm-frontend/src/assets/fonts/InterVariable-Italic.woff2 b/cdrm-frontend/src/assets/fonts/InterVariable-Italic.woff2 new file mode 100644 index 0000000..b3530f3 Binary files /dev/null and b/cdrm-frontend/src/assets/fonts/InterVariable-Italic.woff2 differ diff --git a/cdrm-frontend/src/assets/fonts/InterVariable.woff2 b/cdrm-frontend/src/assets/fonts/InterVariable.woff2 new file mode 100644 index 0000000..5a8d3e7 Binary files /dev/null and b/cdrm-frontend/src/assets/fonts/InterVariable.woff2 differ diff --git a/cdrm-frontend/src/assets/fonts/font-face.css b/cdrm-frontend/src/assets/fonts/font-face.css new file mode 100644 index 0000000..d12308e --- /dev/null +++ b/cdrm-frontend/src/assets/fonts/font-face.css @@ -0,0 +1,15 @@ +@font-face { + font-family: Inter; + src: url("./InterVariable.woff2"); + font-style: normal; + font-weight: 300 900; + font-display: swap; +} + +@font-face { + font-family: Inter; + src: url("./InterVariable-Italic.woff2"); + font-style: italic; + font-weight: 300 900; + font-display: swap; +} diff --git a/cdrm-frontend/src/components/Container.jsx b/cdrm-frontend/src/components/Container.jsx new file mode 100644 index 0000000..d25cc18 --- /dev/null +++ b/cdrm-frontend/src/components/Container.jsx @@ -0,0 +1,9 @@ +const Container = ({ children, className = "", ...props }) => { + return ( +
+ {children} +
+ ); +}; + +export default Container; diff --git a/cdrm-frontend/src/components/NavBar.jsx b/cdrm-frontend/src/components/NavBar.jsx index 88c6034..a8896d9 100644 --- a/cdrm-frontend/src/components/NavBar.jsx +++ b/cdrm-frontend/src/components/NavBar.jsx @@ -1,13 +1,13 @@ import { useEffect, useState } from "react"; import { NavLink } from "react-router-dom"; -import homeIcon from "../assets/icons/home.svg"; -import cacheIcon from "../assets/icons/cache.svg"; -import apiIcon from "../assets/icons/api.svg"; -import testPlayerIcon from "../assets/icons/testplayer.svg"; -import accountIcon from "../assets/icons/account.svg"; -import discordIcon from "../assets/icons/discord.svg"; -import telegramIcon from "../assets/icons/telegram.svg"; -import giteaIcon from "../assets/icons/gitea.svg"; +import { FaDiscord } from "react-icons/fa"; +import { FaTelegram } from "react-icons/fa"; +import { SiGitea } from "react-icons/si"; +import { FaHome } from "react-icons/fa"; +import { FaDatabase } from "react-icons/fa"; +import { IoCodeSlashSharp } from "react-icons/io5"; +import { FaVideo } from "react-icons/fa"; +import { RiAccountCircleFill } from "react-icons/ri"; function NavBar() { const [externalLinks, setExternalLinks] = useState({ @@ -23,152 +23,156 @@ function NavBar() { .catch((error) => console.error("Error fetching links:", error)); }, []); + const MenuItem = ({ to, children }) => { + return ( +
  • + (isActive ? "menu-active" : "")}> + {children} + +
  • + ); + }; + return ( -
    - {/* Header */} -
    -

    - CDRM-Project -

    -
    + <> +
    +
    +
    +
    + + {" "} + {" "} + +
    +
      + + + Home + + + + Cache + + + + API + + + + Test Player + + + + My Account + - {/* Scrollable navigation area */} -
      - {/* Main NavLinks */} - - `flex flex-row p-3 border-l-3 ${ - isActive - ? "border-l-sky-500/50 bg-black/50" - : "hover:border-l-sky-500/50 hover:bg-white/5" - }` - } - > - -

      - Home -

      -
      - - - `flex flex-row p-3 border-l-3 ${ - isActive - ? "border-l-emerald-500/50 bg-black/50" - : "hover:border-l-emerald-500/50 hover:bg-white/5" - }` - } - > - -

      - Cache -

      -
      - - - `flex flex-row p-3 border-l-3 ${ - isActive - ? "border-l-indigo-500/50 bg-black/50" - : "hover:border-l-indigo-500/50 hover:bg-white/5" - }` - } - > - -

      - API -

      -
      - - - `flex flex-row p-3 border-l-3 ${ - isActive - ? "border-l-rose-500/50 bg-black/50" - : "hover:border-l-rose-500/50 hover:bg-white/5" - }` - } - > - -

      - Test Player -

      -
      - - {/* Account link at bottom of scrollable area */} -
      - - `flex flex-row p-3 border-l-3 ${ - isActive - ? "border-l-yellow-500/50 bg-black/50" - : "hover:border-l-yellow-500/50 hover:bg-white/5" - }` - } - > - -

      +

      Social links
      +
    • + + + Discord + +
    • +
    • + + + Telegram + +
    • +
    • + + + Gitea + +
    • +
    +
    + CDRM-Project +
    +
    +
      + + + Home + + + + Cache + + + + API + + + + Test Player + + + My Account -

      - +
      +
    +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    - - {/* External links at very bottom */} -
    - - Discord - - - Telegram - - - Gitea - -
    -
    + ); } diff --git a/cdrm-frontend/src/components/Pages/API.jsx b/cdrm-frontend/src/components/Pages/API.jsx index b43bc55..c42a0bb 100644 --- a/cdrm-frontend/src/components/Pages/API.jsx +++ b/cdrm-frontend/src/components/Pages/API.jsx @@ -1,4 +1,8 @@ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; +import NavBar from "../NavBar"; +import Container from "../Container"; +import { FaCopy } from "react-icons/fa"; +import { toast } from "sonner"; const { protocol, hostname, port } = window.location; @@ -10,6 +14,11 @@ if ( fullHost += `:${port}`; } +const handleCopy = (text) => { + navigator.clipboard.writeText(text); + toast.success("Copied to clipboard"); +}; + function API() { const [deviceInfo, setDeviceInfo] = useState({ device_type: "", @@ -61,13 +70,7 @@ function API() { document.title = "API | CDRM-Project"; }, []); - return ( -
    -
    - Sending a decryption request -
    -
    -                        {`import requests
    +    const decryptRequest = `import requests
     
     print(requests.post(
         url='${fullHost}/api/decrypt',
    @@ -83,55 +86,154 @@ print(requests.post(
                 'Accept-Language': 'en-US,en;q=0.5',
             })
         }
    -).json()['message'])`}
    -                    
    -
    -
    -
    - Sending a search request -
    -
    -                        {`import requests
    +).json()['message'])`;
    +
    +    const searchRequest = `import requests
     
     print(requests.post(
         url='${fullHost}/api/cache/search',
         json={
             'input': 'AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA=='
         }
    -).json())`}
    -                    
    +).json())`; + + return ( + <> + + +
    +
    +
    + +
    + Sending a decryption request +
    +
    +
    +                                    {decryptRequest}
    +                                
    +
    + +
    +
    +
    + +
    + +
    + Sending a search request +
    +
    +
    +                                    {searchRequest}
    +                                
    +
    + +
    +
    +
    + +
    + +
    + PyWidevine RemoteCDM info +
    +
    +

    + Device Type:{" "} + + {deviceInfo.device_type || "N/A"} + +

    +

    + System ID:{" "} + + {deviceInfo.system_id || "N/A"} + +

    +

    + Security Level:{" "} + + {deviceInfo.security_level || "N/A"} + +

    +

    + Host:{" "} + {fullHost}/remotecdm/widevine +

    +

    + Secret:{" "} + {deviceInfo.secret || "N/A"} +

    +

    + Device Name:{" "} + + {deviceInfo.device_name || "N/A"} + +

    +
    +
    + +
    + +
    + PyPlayready RemoteCDM info +
    +
    +

    + Security Level:{" "} + + {prDeviceInfo.security_level || "N/A"} + +

    +

    + Host:{" "} + + {fullHost}/remotecdm/playready + +

    +

    + Secret:{" "} + + {prDeviceInfo.secret || "N/A"} + +

    +

    + Device Name:{" "} + + {prDeviceInfo.device_name || "N/A"} + +

    +
    +
    +
    -
    -
    - PyWidevine RemoteCDM info -
    -

    - Device Type: '{deviceInfo.device_type}'
    - System ID: {deviceInfo.system_id} -
    - Security Level: {deviceInfo.security_level} -
    - Host: {fullHost}/remotecdm/widevine -
    - Secret: '{deviceInfo.secret}'
    - Device Name: {deviceInfo.device_name} -

    -
    -
    -
    - PyPlayready RemoteCDM info -
    -

    - Security Level: {prDeviceInfo.security_level} -
    - Host: {fullHost}/remotecdm/playready -
    - Secret: '{prDeviceInfo.secret}'
    - Device Name: {prDeviceInfo.device_name} -

    -
    -
    -
    + + ); } diff --git a/cdrm-frontend/src/components/Pages/Account.jsx b/cdrm-frontend/src/components/Pages/Account.jsx index e5c865f..f31d2c6 100644 --- a/cdrm-frontend/src/components/Pages/Account.jsx +++ b/cdrm-frontend/src/components/Pages/Account.jsx @@ -1,14 +1,17 @@ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; +import { toast } from "sonner"; +import Container from "../Container"; +import NavBar from "../NavBar"; +import MyAccount from "./MyAccount"; import Register from "./Register"; -import MyAccount from "./MyAccount"; // <-- Import the MyAccount component function Account() { - const [isLoggedIn, setIsLoggedIn] = useState(null); // null = loading state + const [isLoggedIn, setIsLoggedIn] = useState(null); useEffect(() => { fetch("/login/status", { method: "POST", - credentials: "include", // Sends cookies with request + credentials: "include", }) .then((res) => res.json()) .then((data) => { @@ -19,19 +22,25 @@ function Account() { } }) .catch((err) => { + toast.error(`Error checking login status. Reason: ${err.message}`); console.error("Error checking login status:", err); - setIsLoggedIn(false); // Assume not logged in on error + setIsLoggedIn(false); }); }, []); if (isLoggedIn === null) { - return
    Loading...
    ; // Optional loading UI + return
    Loading...
    ; } return ( -
    - {isLoggedIn ? : } -
    + <> + + +
    + {isLoggedIn ? : } +
    +
    + ); } diff --git a/cdrm-frontend/src/components/Pages/Cache.jsx b/cdrm-frontend/src/components/Pages/Cache.jsx index 9522a4e..ce878b8 100644 --- a/cdrm-frontend/src/components/Pages/Cache.jsx +++ b/cdrm-frontend/src/components/Pages/Cache.jsx @@ -1,9 +1,16 @@ import { useEffect, useRef, useState } from "react"; +import { FaDownload } from "react-icons/fa"; +import { toast } from "sonner"; +import Container from "../Container"; +import NavBar from "../NavBar"; function Cache() { const [searchQuery, setSearchQuery] = useState(""); const [cacheData, setCacheData] = useState([]); - const [keyCount, setKeyCount] = useState(0); // New state to store the key count + const [keyCount, setKeyCount] = useState(0); + const [loading, setLoading] = useState(false); + const [hasSearched, setHasSearched] = useState(false); + const debounceTimeout = useRef(null); // Fetch the key count when the component mounts @@ -23,32 +30,40 @@ function Cache() { const handleInputChange = (event) => { const query = event.target.value; - setSearchQuery(query); // Update the search query + setSearchQuery(query); - // Clear the previous timeout if (debounceTimeout.current) { clearTimeout(debounceTimeout.current); } - // Set a new timeout to send the API call after 1 second of no typing - debounceTimeout.current = setTimeout(() => { - if (query.trim() !== "") { - sendApiCall(query); // Only call the API if the query is not empty - } else { - setCacheData([]); // Clear results if query is empty - } - }, 1000); // 1 second delay + if (query.trim() !== "") { + setLoading(true); // Show spinner immediately + debounceTimeout.current = setTimeout(() => { + sendApiCall(query); + }, 1000); + } else { + setHasSearched(false); // Reset state when input is cleared + setCacheData([]); + } }; const sendApiCall = (text) => { + setLoading(true); fetch("/api/cache/search", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ input: text }), }) .then((response) => response.json()) - .then((data) => setCacheData(data)) // Update cache data with the results - .catch((error) => console.error("Error:", error)); + .then((data) => { + setCacheData(data); + setHasSearched(true); + }) + .catch((error) => { + toast.error(`Error: ${error.message}`); + console.error("Error:", error); + }) + .finally(() => setLoading(false)); }; useEffect(() => { @@ -56,51 +71,71 @@ function Cache() { }, []); return ( -
    -
    - - - Download Cache - -
    -
    - - - - - - - - - - {cacheData.length > 0 ? ( - cacheData.map((item, index) => ( - - - - - - )) - ) : ( - - - - )} - -
    PSSHKIDKey
    {item.PSSH}{item.KID}{item.Key}
    - No data found -
    -
    -
    + <> + + +
    +
    + +
    + +
    + + {loading ? ( +
    + + Searching... +
    + ) : cacheData.length > 0 ? ( +
    +
    + + + + + + + + + + {cacheData.map((item, index) => ( + + + + + + ))} + +
    PSSHkey ID:key pair
    {index + 1}{item.PSSH} + {item.KID}:{item.Key} +
    +
    +
    + ) : hasSearched ? ( +
    +
    No data found in the database
    +
    + ) : ( +
    +
    Enter a search query to see results
    +
    + )} +
    + ); } diff --git a/cdrm-frontend/src/components/Pages/HomePage.jsx b/cdrm-frontend/src/components/Pages/HomePage.jsx index 09a2f29..2704ef9 100644 --- a/cdrm-frontend/src/components/Pages/HomePage.jsx +++ b/cdrm-frontend/src/components/Pages/HomePage.jsx @@ -1,5 +1,9 @@ -import React, { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { readTextFromClipboard } from "../Functions/ParseChallenge"; +import NavBar from "../NavBar"; +import Container from "../Container"; +import { toast } from "sonner"; +import { IoInformationCircleOutline } from "react-icons/io5"; function HomePage() { const [pssh, setPssh] = useState(""); @@ -58,7 +62,7 @@ function HomePage() { }) .catch((error) => { console.error("Error during decryption request:", error); - setMessage("Error: Unable to process request."); + setMessage(`Error: Unable to process request. Reason: ${error.message}`); setIsVisible(true); }); }; @@ -67,14 +71,15 @@ function HomePage() { event.preventDefault(); if (messageRef.current) { const textToCopy = messageRef.current.innerText; // Grab the plain text (with visual line breaks) + toast.success("Copied to clipboard"); navigator.clipboard.writeText(textToCopy).catch((err) => { - alert("Failed to copy!"); + toast.error(`Failed to copy. Reason: ${err.message}`); console.error(err); }); } }; - const handleFetchPaste = () => { + const handleFetchPaste = (event) => { event.preventDefault(); readTextFromClipboard() .then(() => { @@ -84,7 +89,8 @@ function HomePage() { setData(document.getElementById("data").value); }) .catch((err) => { - alert("Failed to paste from fetch!"); + toast.error(`Failed to paste from fetch. Reason: ${err.message}`); + console.error("Failed to paste from fetch:", err); }); }; @@ -133,138 +139,150 @@ function HomePage() { return ( <> -
    -
    - - setPssh(e.target.value)} - /> - - setLicurl(e.target.value)} - /> - - setProxy(e.target.value)} - /> - -