From 4ae6b278099394c8488af1e8c357f3d3e1724671 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Sat, 8 Aug 2026 17:35:53 +0200 Subject: [PATCH] Initial commit --- background.js | 13 +++++++ content.css | 6 +++ content.js | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++ manifest.json | 18 +++++++++ 4 files changed, 137 insertions(+) create mode 100644 background.js create mode 100644 content.css create mode 100644 content.js create mode 100644 manifest.json diff --git a/background.js b/background.js new file mode 100644 index 0000000..85cd086 --- /dev/null +++ b/background.js @@ -0,0 +1,13 @@ +browser.action.onClicked.addListener((tab) => { + // Inject the CSS for the hover highlight + browser.scripting.insertCSS({ + target: { tabId: tab.id }, + files: ["content.css"] + }); + + // Inject the JavaScript for the element picker + browser.scripting.executeScript({ + target: { tabId: tab.id }, + files: ["content.js"] + }); +}); diff --git a/content.css b/content.css new file mode 100644 index 0000000..ed22de3 --- /dev/null +++ b/content.css @@ -0,0 +1,6 @@ +.pep-hover-highlight { + outline: 3px dashed #0088ff !important; + outline-offset: -3px !important; + cursor: crosshair !important; + background-color: rgba(0, 136, 255, 0.1) !important; +} diff --git a/content.js b/content.js new file mode 100644 index 0000000..72757d8 --- /dev/null +++ b/content.js @@ -0,0 +1,100 @@ +(function() { + if (window.pepActive) return; + window.pepActive = true; + + let currentHovered = null; + + function handleMouseOver(e) { + e.stopPropagation(); + if (currentHovered) { + currentHovered.classList.remove('pep-hover-highlight'); + } + currentHovered = e.target; + currentHovered.classList.add('pep-hover-highlight'); + } + + function handleMouseOut(e) { + e.stopPropagation(); + if (currentHovered) { + currentHovered.classList.remove('pep-hover-highlight'); + currentHovered = null; + } + } + + function handleClick(e) { + e.preventDefault(); + e.stopPropagation(); + + // Turn off picker mode + document.removeEventListener('mouseover', handleMouseOver, true); + document.removeEventListener('mouseout', handleMouseOut, true); + document.removeEventListener('click', handleClick, true); + window.pepActive = false; + + if (currentHovered) { + currentHovered.classList.remove('pep-hover-highlight'); + printElement(currentHovered); + } + } + + function printElement(el) { + el.classList.add('pep-print-target'); + + // Walk up the DOM tree to tag all ancestors of the target + let current = el.parentElement; + const ancestors = []; + while (current && current !== document.body && current !== document.documentElement) { + current.classList.add('pep-print-ancestor'); + ancestors.push(current); + current = current.parentElement; + } + + // Inject CSS that hides siblings of the target and siblings of its ancestors + const style = document.createElement('style'); + style.textContent = ` + @media print { + /* Hide all siblings of the body, ancestors, and target */ + body > *:not(.pep-print-ancestor):not(.pep-print-target), + .pep-print-ancestor > *:not(.pep-print-ancestor):not(.pep-print-target) { + display: none !important; + } + + /* Strip formatting from ancestors to prevent them from pushing the target around */ + .pep-print-ancestor { + padding: 0 !important; + margin: 0 !important; + border: none !important; + background: transparent !important; + box-shadow: none !important; + position: static !important; + overflow: visible !important; + } + + /* Reset body constraints */ + body { + padding: 0 !important; + margin: 0 !important; + min-height: 100vh !important; + } + } + `; + document.head.appendChild(style); + + // Short delay to ensure browser applies the print CSS before opening dialog + setTimeout(() => { + window.print(); + + // Clean up the temporary styles and classes after the print dialog closes + setTimeout(() => { + el.classList.remove('pep-print-target'); + ancestors.forEach(a => a.classList.remove('pep-print-ancestor')); + style.remove(); + }, 1000); + }, 100); + } + + // Activate event listeners in the capture phase to override default page behaviors + document.addEventListener('mouseover', handleMouseOver, true); + document.addEventListener('mouseout', handleMouseOut, true); + document.addEventListener('click', handleClick, true); +})(); diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..dcca74c --- /dev/null +++ b/manifest.json @@ -0,0 +1,18 @@ +{ + "manifest_version": 3, + "name": "Print Element Picker", + "version": "1.0", + "description": "Interactively pick an element on the page and print only that element.", + "permissions": ["activeTab", "scripting"], + "action": { + "default_title": "Pick element to print" + }, + "background": { + "scripts": ["background.js"] + }, + "browser_specific_settings": { + "gecko": { + "id": "print-picker@yourdomain.com" + } + } +}