From a172d00b9ea0cbb8d5a3d472ce9c0e83a92a2cfd Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Mon, 13 Oct 2025 19:11:02 +0200 Subject: [PATCH] WIP --- .../blueprint/ui/templates/diff.html | 12 +- .../static/js/snippet-to-image.js | 122 ++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 changedetectionio/static/js/snippet-to-image.js diff --git a/changedetectionio/blueprint/ui/templates/diff.html b/changedetectionio/blueprint/ui/templates/diff.html index df4bf03fe..f1efed8ed 100644 --- a/changedetectionio/blueprint/ui/templates/diff.html +++ b/changedetectionio/blueprint/ui/templates/diff.html @@ -10,6 +10,8 @@ const highlight_submit_ignore_url="{{url_for('ui.ui_edit.highlight_submit_ignore_url', uuid=uuid)}}"; + +
@@ -94,6 +96,14 @@
+ {% if password_enabled_and_share_is_off %}
Pro-tip: You can enable "share access when password is enabled" from settings
{% endif %} @@ -103,7 +113,7 @@ -
{{ content| diff_unescape_difference_spans  }}
+
{{ content| diff_unescape_difference_spans  }}
diff --git a/changedetectionio/static/js/snippet-to-image.js b/changedetectionio/static/js/snippet-to-image.js new file mode 100644 index 000000000..196a7dc6b --- /dev/null +++ b/changedetectionio/static/js/snippet-to-image.js @@ -0,0 +1,122 @@ +/** + * snippet-to-image.js + * Converts the selected diff content to a JPEG image for sharing/downloading + */ + +async function diffToJpeg() { + // Check if html2canvas is loaded + if (typeof html2canvas === 'undefined') { + alert("html2canvas library is not loaded yet. Please wait a moment and try again."); + return; + } + + // Get the user's text selection + const selection = window.getSelection(); + if (!selection || selection.rangeCount === 0 || selection.isCollapsed) { + alert("Please select the text/lines you want to capture first by highlighting with your mouse."); + return; + } + + // Show a loading indicator + const btn = document.getElementById("share-as-image-btn"); + const originalText = btn ? btn.innerHTML : ''; + if (btn) { + btn.innerHTML = 'Generating...'; + btn.style.opacity = "0.5"; + btn.style.pointerEvents = "none"; + } + + let tempElement = null; + + try { + // Get the selected content with HTML (preserves spans and styling) + const range = selection.getRangeAt(0); + const selectedFragment = range.cloneContents(); + + // Create a temporary element to hold the selected content + tempElement = document.createElement("pre"); + tempElement.id = "temp-capture-element"; + tempElement.appendChild(selectedFragment); + + // Copy styles from the original #difference element + const originalPre = document.getElementById("difference"); + const originalStyles = window.getComputedStyle(originalPre); + + tempElement.style.position = "absolute"; + tempElement.style.left = "-9999px"; + tempElement.style.top = "0"; + tempElement.style.whiteSpace = "pre-wrap"; + tempElement.style.fontFamily = originalStyles.fontFamily; + tempElement.style.fontSize = originalStyles.fontSize; + tempElement.style.lineHeight = originalStyles.lineHeight; + tempElement.style.color = originalStyles.color; + tempElement.style.backgroundColor = "#ffffff"; + tempElement.style.padding = "10px"; + tempElement.style.border = originalStyles.border; + tempElement.style.width = "auto"; + tempElement.style.maxWidth = originalPre.offsetWidth + "px"; + + // Add to document so it can be measured and captured + document.body.appendChild(tempElement); + + console.log("Capturing selection..."); + console.log("Temp element dimensions:", tempElement.offsetWidth, "x", tempElement.offsetHeight); + + // Give browser time to render + await new Promise(resolve => setTimeout(resolve, 50)); + + const canvas = await html2canvas(tempElement, { + scale: 2, // higher quality + useCORS: true, + allowTaint: true, + logging: false, + backgroundColor: '#ffffff', + scrollX: 0, + scrollY: 0 + }); + + console.log("Canvas created:", canvas.width, "x", canvas.height); + + if (canvas.width === 0 || canvas.height === 0) { + throw new Error("Canvas is empty - no content captured"); + } + + // Convert to JPEG + const jpeg = canvas.toDataURL("image/jpeg", 0.95); + + if (jpeg === "data:," || jpeg.length < 100) { + throw new Error("Failed to generate image data"); + } + + // Open in new window + const win = window.open(); + if (win) { + win.document.write('Diff ScreenshotDiff Screenshot'); + } else { + // Fallback to download if popup blocked + const a = document.createElement("a"); + a.href = jpeg; + a.download = "diff-snapshot-" + Date.now() + ".jpg"; + a.click(); + } + + // Clear the selection + selection.removeAllRanges(); + + } catch (error) { + console.error("Error generating image:", error); + alert("Failed to generate image: " + error.message); + } finally { + // Clean up temporary element + if (tempElement && tempElement.parentNode) { + tempElement.parentNode.removeChild(tempElement); + } + + // Reset button state + if (btn) { + btn.innerHTML = originalText; + btn.style.opacity = "1"; + btn.style.pointerEvents = "auto"; + } + } +}