From 330c6937484e8a5ef4d8deaacc0a12f8de002f3d Mon Sep 17 00:00:00 2001 From: lishhh06 Date: Tue, 26 May 2026 17:24:24 +0530 Subject: [PATCH] added export image feature --- app/page.tsx | 107 ++++++++++++++++++++++++++++++++++++++++++++-- fix_syntax.py | 50 ++++++++++++++++++++++ fix_syntax2.py | 24 +++++++++++ fix_syntax3.py | 36 ++++++++++++++++ next.config.ts | 19 +++----- package-lock.json | 50 ++++++++++++++++++++++ package.json | 1 + 7 files changed, 269 insertions(+), 18 deletions(-) create mode 100644 fix_syntax.py create mode 100644 fix_syntax2.py create mode 100644 fix_syntax3.py diff --git a/app/page.tsx b/app/page.tsx index d3c5c18..d62e9c0 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,4 +1,4 @@ -"use client" +"use client" const safeBase64Encode = (str: string) => btoa(unescape(encodeURIComponent(str))); @@ -24,6 +24,7 @@ import { Moon, Link as LinkIcon, Timer, + Camera, } from "lucide-react" import { toast } from "sonner" @@ -1102,6 +1103,7 @@ export default function CodeEditor() { const [activeTab, setActiveTab] = useState("html") const [theme, setTheme] = useState<"light" | "dark">("light") const previewRef = useRef(null) + const codeRef = useRef(code) useEffect(() => { const savedTheme = localStorage.getItem("theme") as "light" | "dark" | null @@ -1116,6 +1118,7 @@ export default function CodeEditor() { }, []) useEffect(() => { + codeRef.current = code const timer = setTimeout(() => { try { localStorage.setItem("webify_code", JSON.stringify(code)) @@ -1149,6 +1152,92 @@ export default function CodeEditor() { setCode(template.content) toast.success(`${template.name} loaded`) } + const exportImage = async () => { + const previewIframe = previewRef.current + if (!previewIframe) { + toast.error("No preview to capture") + return + } + + const loadingToast = toast.loading("Capturing preview…") + + try { + const { default: html2canvas } = await import("html2canvas") + + const width = previewIframe.clientWidth || 800 + const height = previewIframe.clientHeight || 600 + const currentCode = codeRef.current + + const combinedHtml = `${currentCode.html}` + // Off-screen but NOT visibility:hidden — hidden iframes don't paint, + // so html2canvas captures a blank canvas + const tempIframe = document.createElement("iframe") + tempIframe.style.cssText = `position:fixed;left:-9999px;top:0;width:${width}px;height:${height}px;border:none;` + document.body.appendChild(tempIframe) + tempIframe.srcdoc = combinedHtml + + // Wait for iframe to load + await new Promise((resolve) => { + tempIframe.addEventListener("load", () => resolve(), { once: true }) + setTimeout(resolve, 4000) + }) + + // Extra time for JS inside the iframe to paint + await new Promise((r) => setTimeout(r, 500)) + + const tempDoc = tempIframe.contentDocument + if (!tempDoc?.body) { + document.body.removeChild(tempIframe) + toast.dismiss(loadingToast) + toast.error("Export failed", { description: "Preview not accessible." }) + return + } + + const canvas = await html2canvas(tempDoc.documentElement, { + useCORS: true, + allowTaint: true, + backgroundColor: "#ffffff", + width, + height, + logging: false, + scale: 1, + }) + + document.body.removeChild(tempIframe) + toast.dismiss(loadingToast) + + // Wrap toBlob in a Promise — plain callback silently swallows failures + const blob = await new Promise((resolve) => { + canvas.toBlob(resolve, "image/png") + }) + + if (!blob) { + toast.error("Export failed", { description: "Canvas was empty or tainted." }) + return + } + + const url = URL.createObjectURL(blob) + const a = document.createElement("a") + a.href = url + a.download = "webify-preview.png" + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(url) + toast.success("Image exported!", { description: "Saved as webify-preview.png" }) + + } catch (err) { + console.error("Export image error:", err) + toast.dismiss(loadingToast) + toast.error("Export failed", { + description: err instanceof Error ? err.message : "Unknown error", + }) + } +} const downloadCode = async () => { const zip = new JSZip() @@ -1239,9 +1328,19 @@ export default function CodeEditor() {
- - Live Preview -
+ + Live Preview + +