This commit is contained in:
dgtlmoon
2025-10-13 19:11:02 +02:00
parent 97b0e12fd3
commit a172d00b9e
2 changed files with 133 additions and 1 deletions
@@ -10,6 +10,8 @@
const highlight_submit_ignore_url="{{url_for('ui.ui_edit.highlight_submit_ignore_url', uuid=uuid)}}";
</script>
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
<script src="{{url_for('static_content', group='js', filename='snippet-to-image.js')}}"></script>
<script src="{{url_for('static_content', group='js', filename='diff-overview.js')}}" defer></script>
<div id="settings">
@@ -94,6 +96,14 @@
</div>
<div class="tab-pane-inner" id="text">
<button id="share-as-image-btn" onclick="diffToJpeg()" title="Share diff as image" style="float: right; margin: 10px; padding: 8px 12px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
<circle cx="8.5" cy="8.5" r="1.5"></circle>
<polyline points="21 15 16 10 5 21"></polyline>
</svg>
Share as Image
</button>
{% if password_enabled_and_share_is_off %}
<div class="tip">Pro-tip: You can enable <strong>"share access when password is enabled"</strong> from settings</div>
{% endif %}
@@ -103,7 +113,7 @@
<tbody>
<tr>
<td id="diff-col" class="highlightable-filter">
<pre style="border-left: 2px solid #ddd;">{{ content| diff_unescape_difference_spans }}</pre>
<pre id="difference" style="border-left: 2px solid #ddd;">{{ content| diff_unescape_difference_spans }}</pre>
</td>
</tr>
</tbody>
@@ -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('<html><head><title>Diff Screenshot</title></head><body style="margin:0;"><img src="' + jpeg + '" alt="Diff Screenshot" style="max-width:100%;"/></body></html>');
} 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";
}
}
}