මේ විදිහට folder එකක් හදන්න
ඒක ඇතුලේ "icons" folder එකත් හදන්න images වලට paint වලින් මොනවා හරි png ටිකක් හදලා resolutions දාන්න match වෙන්න
open-original-image/
├── manifest.json # Extension manifest (Manifest V3)
├── background.js # Service worker — creates context menu, handles clicks
├── content.js # Content script — finds the real image under cursor
└── icons/
├── icon16.png # resolution 16x16
├── icon48.png # resolution 48x48
└── icon128.png # resolution 128x128
මේ files note pad එකෙන් හදලා හරි extension එක්ක save කරන්න
manifest.json
background.js
content.js
You can then load it in Chrome via chrome://extensions → Developer mode → Load unpacked → point to that folder.
ඒක ඇතුලේ "icons" folder එකත් හදන්න images වලට paint වලින් මොනවා හරි png ටිකක් හදලා resolutions දාන්න match වෙන්න
open-original-image/
├── manifest.json # Extension manifest (Manifest V3)
├── background.js # Service worker — creates context menu, handles clicks
├── content.js # Content script — finds the real image under cursor
└── icons/
├── icon16.png # resolution 16x16
├── icon48.png # resolution 48x48
└── icon128.png # resolution 128x128
මේ files note pad එකෙන් හදලා හරි extension එක්ක save කරන්න
manifest.json
JSON:
{
"manifest_version": 3,
"name": "Open Original Image — Instagram",
"version": "1.0",
"description": "Right-click any image on Instagram and open the full-resolution original in a new tab.",
"permissions": ["contextMenus", "activeTab"],
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
},
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["*://*.instagram.com/*"],
"js": ["content.js"],
"run_at": "document_idle"
}
]
}
background.js
JavaScript:
// Create context menu item on install
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: "open-original-image",
title: "Open Original Image in New Tab",
contexts: ["all"],
documentUrlPatterns: ["*://*.instagram.com/*"]
});
});
// Handle context menu click
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId !== "open-original-image") return;
// Ask the content script for the image URL at the last right-click position
chrome.tabs.sendMessage(tab.id, { action: "getImageUrl" }, (response) => {
if (chrome.runtime.lastError) {
console.warn("Content script not reachable:", chrome.runtime.lastError.message);
return;
}
if (response && response.url) {
chrome.tabs.create({ url: response.url, active: false });
}
});
});
content.js
JavaScript:
/**
* Instagram Image Finder — Content Script
*
* Tracks the last right-click position, then on demand walks
* the DOM stack at those coordinates to find the real <img> element
* (even under Instagram's overlay divs) and returns the highest
* resolution URL from srcset.
*/
let lastX = 0;
let lastY = 0;
// Track every contextmenu event to know exactly where the user right-clicked
document.addEventListener("contextmenu", (e) => {
lastX = e.clientX;
lastY = e.clientY;
}, true);
/**
* Parse a srcset string and return the URL with the largest width descriptor.
* Falls back to the last entry if no width descriptors are found.
*/
function getHighestResSrcsetUrl(srcset) {
if (!srcset) return null;
const candidates = srcset.split(",").map((entry) => {
const parts = entry.trim().split(/\s+/);
const url = parts[0];
const descriptor = parts[1] || "";
const width = parseInt(descriptor, 10) || 0;
return { url, width };
});
if (candidates.length === 0) return null;
// Sort descending by width, pick the largest
candidates.sort((a, b) => b.width - a.width);
return candidates[0].url;
}
/**
* Given a DOM element, try to extract the best image URL from it.
* Checks: <img> src/srcset, CSS background-image.
*/
function extractImageUrl(el) {
if (!el) return null;
// Direct <img> element
if (el.tagName === "IMG") {
const srcsetUrl = getHighestResSrcsetUrl(el.srcset);
if (srcsetUrl) return srcsetUrl;
if (el.src) return el.src;
}
// CSS background-image
const bg = getComputedStyle(el).backgroundImage;
if (bg && bg !== "none") {
const match = bg.match(/url\(["']?(.*?)["']?\)/);
if (match && match[1]) return match[1];
}
return null;
}
/**
* Main finder: walks through all elements at the click point,
* then searches their subtrees for <img> elements.
*/
function findImageAtPoint(x, y) {
// 1. Get all elements stacked at this coordinate (pierces overlays)
const stack = document.elementsFromPoint(x, y);
// 2. First pass — check if any element in the stack IS an <img>
for (const el of stack) {
const url = extractImageUrl(el);
if (url) return url;
}
// 3. Second pass — for each element in the stack, look for <img> children
// This catches Instagram's pattern: overlay div sits above, <img> is a sibling/child
for (const el of stack) {
// Check the element's parent container for nearby <img> tags
const container = el.closest("div");
if (container) {
const imgs = container.querySelectorAll("img");
for (const img of imgs) {
const url = extractImageUrl(img);
if (url) return url;
}
}
}
// 4. Third pass — walk up the tree further, looking for the post/story container
for (const el of stack) {
// Instagram uses <article> for feed posts
const article = el.closest("article");
if (article) {
// Find all images within the article, pick the one closest to click point
const imgs = article.querySelectorAll("img");
let bestUrl = null;
let bestDistance = Infinity;
for (const img of imgs) {
// Skip tiny images (profile pics, icons — usually < 40px)
if (img.naturalWidth > 0 && img.naturalWidth < 40) continue;
if (img.width < 40 && img.height < 40) continue;
const rect = img.getBoundingClientRect();
// Check if click point is inside this image's bounds
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
const url = extractImageUrl(img);
if (url) return url;
}
// Otherwise measure distance
const cx = rect.left + rect.width / 2;
const cy = rect.top + rect.height / 2;
const dist = Math.hypot(x - cx, y - cy);
if (dist < bestDistance) {
bestDistance = dist;
bestUrl = extractImageUrl(img);
}
}
if (bestUrl) return bestUrl;
}
}
return null;
}
// Listen for messages from the background service worker
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "getImageUrl") {
const url = findImageAtPoint(lastX, lastY);
sendResponse({ url: url || null });
}
return true; // keep channel open for async response
});
You can then load it in Chrome via chrome://extensions → Developer mode → Load unpacked → point to that folder.