<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="author" content="@ajotanc" /> <title>Pixel Magic Tool (Inline) - @ajotanc</title> <link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAK9JREFUeNqUUssNwyAMJZWVUw4dhRHakZA6RqWMFEbwKDnk1FNBekEWxOBYQggL/D68yXXq+M7PtHkcefn89vrOw/UrP96w/NUFGiDLRz71GyY0QJa1Yn+nFa0ShqUNYCAF0QvoceOB4naEZif6UTNRapYaTyauRi4DEspr4Hbs5YKsbmtMyeJ0LxeESV4gB+hlSy4oO2txWysyus0a0+lO6vBjxcTMlG4mt2H6F2AAhU5NWu4dorQAAAAASUVORK5CYII= " /> <style> body { display: flex; min-height: 100vh; margin: 0; font-size: 14px; font-family: monospace !important; } #code { width: calc(100% - 40px); height: 100%; padding: 20px; margin: 0; white-space: pre-wrap; word-wrap: break-word; } #toast-container { position: fixed; bottom: 20px; right: 20px; z-index: 9999; } .toast { display: flex; align-items: center; width: max-content; padding: 6px 12px; margin: 10px 0 0; border-radius: 8px; color: #fff; font-size: 12px; transform: translateY(30px); opacity: 0; visibility: hidden; } .toast .toast-body { padding: 8px 0; color: #fdfdfd; } .toast.success { background-color: #8ac926; } .toast.error { background-color: #ff595e; } .toast.warning { background-color: #f48c06; } .toast-progress { position: absolute; left: 4px; bottom: 4px; width: calc(100% - 8px); height: 3px; transform: scaleX(0); transform-origin: left; border-radius: inherit; } .toast.success .toast-progress { background: linear-gradient(to right, #8ac926, #548c2f); } .toast.error .toast-progress { background: linear-gradient(to right, #ff595e, #c9262b); } .toast.warning .toast-progress { background: linear-gradient(to right, #f48c06, #e85d04); } @keyframes progress { to { transform: scaleX(1); } } @keyframes fadeIn { 5% { opacity: 1; visibility: visible; transform: translateY(0); } 95% { opacity: 1; transform: translateY(0); } } </style> </head> <body> <pre id="code"></pre> <div id="toast-container"></div> </body> <script> const protocol = window.location.protocol === "file:" ? "http:" : window.location.protocol; const query = window.location.search; const params = new URLSearchParams(query); const hostname = params.get("hostname") ? params.get("hostname") : params.get("hn") ? params.get("hn") : "0.0.0.0"; async function execute() { const image = params.get("image"); if (!image) { toast("Image parameter is required!", "error"); return; } if (!hostname) { toast("Hostname parameter is required!", "error"); return; } const path = image.match(/^https?:\/\//) ? image : `${protocol}//${hostname}/${image}`; const response = await fetch(path); const blob = await response.blob(); const body = new FormData(); const file = new File([blob], "image", { type: blob.type }); body.append("image", file); await convertImage(body, query); } async function convertImage(body, query) { const mode = params.get("mode") ? params.get("mode") : "production"; const filename = params.get("image").replace(/\.[^.]+$/, ""); const output = params.get("output") ? params.get("output") : params.get("o") ? params.get("o") : "json"; const file = params.get("file") && params.get("file") === "true" ? true : false; const simulate = params.get("simulate") && params.get("simulate") === "true" ? true : false; const url = mode === "dev" ? `http://localhost:3333/api/wled/image${query}` : `https://pixelmagictool.vercel.app/api/wled/image${query}`; try { const response = await fetch(url, { method: "POST", body: body, }); const data = await response.text(); if (!response.ok) { const { issues } = JSON.parse(data); Object.entries(issues).forEach((issue) => { const [error, message] = issue; if (error !== "_errors") { toast(`Error: [${error}] ${message["_errors"]}`, "error"); } }); return; } if (file) { downloadFile(data, filename, output); } if (simulate && output === "json") { await requestSimulate(data); } const code = document.getElementById("code"); code.textContent = data; } catch (error) { toast(error, "error"); } } async function requestSimulate(body) { const url = `${protocol}//${hostname}/json/state`; try { const response = await fetch(url, { method: "POST", body, }); const { ok, status, statusText } = response; if (!ok) { toast(`Error: [${status}] ${statusText}`, "error"); return; } const { success } = await response.json(); if (success) { toast("Preset sent successfully!"); } } catch (error) { toast(error, "error"); } } function downloadFile(text, filename, output) { let mimeType; let fileExtension; let response; switch (output) { case "json": mimeType = "application/json"; fileExtension = "json"; break; case "ha": mimeType = "application/x-yaml"; fileExtension = "yaml"; break; case "curl": mimeType = "text/plain"; fileExtension = "txt"; break; } const blob = new Blob([text], { type: mimeType }); const url = URL.createObjectURL(blob); const anchorElement = document.createElement("a"); anchorElement.href = url; anchorElement.download = `${filename}.${fileExtension}`; anchorElement.click(); URL.revokeObjectURL(url); } function toast(message, type = "success", duration = 2000) { const toast = document.createElement("div"); const wait = 100; toast.style.animation = "fadeIn"; toast.style.animationDuration = `${duration + wait}ms`; toast.style.animationTimingFunction = "linear"; toast.classList.add("toast", type); const body = document.createElement("span"); body.classList.add("toast-body"); body.textContent = message; toast.appendChild(body); const progress = document.createElement("div"); progress.classList.add("toast-progress"); progress.style.animation = "progress"; progress.style.animationDuration = `${duration + wait}ms`; progress.style.animationDelay = "0s"; toast.appendChild(progress); document.getElementById("toast-container").appendChild(toast); setTimeout(() => { toast.style.opacity = 0; setTimeout(() => { toast.remove(); }, wait); }, duration); } (async () => { await execute(); })(); // window.addEventListener("load", async () => { // await execute(); // }); </script> </html>