From 44d5b62b3c8533bff09f59b07274fad4fc4e2c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Wed, 23 Apr 2025 12:43:41 +0400 Subject: [PATCH] GM --- background.js | 28 +++++++++++++++++++++++ content.js | 20 +++++++++++++++++ icons/nostr-48.png | Bin 0 -> 1775 bytes icons/nostr-96.png | Bin 0 -> 3430 bytes icons/nostr.svg | 9 ++++++++ manifest.json | 34 ++++++++++++++++++++++++++++ popup/nostr-links.css | 49 +++++++++++++++++++++++++++++++++++++++++ popup/nostr-links.html | 13 +++++++++++ popup/nostr-links.js | 20 +++++++++++++++++ 9 files changed, 173 insertions(+) create mode 100644 background.js create mode 100644 content.js create mode 100644 icons/nostr-48.png create mode 100644 icons/nostr-96.png create mode 100644 icons/nostr.svg create mode 100644 manifest.json create mode 100644 popup/nostr-links.css create mode 100644 popup/nostr-links.html create mode 100644 popup/nostr-links.js diff --git a/background.js b/background.js new file mode 100644 index 0000000..82c5d06 --- /dev/null +++ b/background.js @@ -0,0 +1,28 @@ + +browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { + if (changeInfo.status !== 'complete') { + browser.pageAction.hide(tabId); + await browser.storage.local.remove(`nl_tab_${sender.tab.id}`); + return; + } +}, { properties: ["status"] }); + +browser.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.action === "showNostrLinksPageAction" && sender.tab?.id) { + const tabLinks = { [`nl_tab_${sender.tab.id}`]: message.links }; + browser.storage.local.set(tabLinks, () => { + browser.pageAction.show(sender.tab.id); + }); + } + + if (message.action === "getNostrLinks" && message.tabId) { + browser.storage.local.get(`nl_tab_${message.tabId}`, (result) => { + sendResponse({ links: result[`nl_tab_${message.tabId}`] || [] }); + }); + return true; // Keep message channel open for async response + } +}); + +browser.tabs.onRemoved.addListener((tabId) => { + browser.storage.local.remove(`nl_tab_${tabId}`); +}); diff --git a/content.js b/content.js new file mode 100644 index 0000000..254bec1 --- /dev/null +++ b/content.js @@ -0,0 +1,20 @@ +document.addEventListener('DOMContentLoaded', function() { + const alternateLinks = document.querySelectorAll('link[rel="alternate"][type="application/nostr+json"]'); + + if (alternateLinks.length > 0) { + console.debug("[nostr-links] Found:", alternateLinks); + + const links = Array.from(alternateLinks).map((link, index) => ({ + uri: link.href.replace(/^(nostr|web+nostr):/, ""), + text: link.title.trim() + // icon: "icon.png" + })); + + browser.runtime.sendMessage({ + action: "showNostrLinksPageAction", + links: links + }); + } else { + console.debug("[nostr-links] No nostr links found"); + } +}); diff --git a/icons/nostr-48.png b/icons/nostr-48.png new file mode 100644 index 0000000000000000000000000000000000000000..06fa7b8bd62166f6d09efa3c6714d6590f8493de GIT binary patch literal 1775 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 IM6N<$g2v&HTmS$7 literal 0 HcmV?d00001 diff --git a/icons/nostr.svg b/icons/nostr.svg new file mode 100644 index 0000000..ff7a7a5 --- /dev/null +++ b/icons/nostr.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..aa1c8ec --- /dev/null +++ b/manifest.json @@ -0,0 +1,34 @@ +{ + "manifest_version": 3, + "name": "Nostr Links", + "version": "1.0.0", + "description": "A web extension to discover Nostr links", + "author": "Râu Cao", + "homepage_url": "https://gitea.kosmos.org/raucao/nostr-links", + "icons": { + "48": "icons/nostr-48.png", + "96": "icons/nostr-96.png" + }, + "permissions": [ + "storage", + "tabs", + "activeTab" + ], + "background": { + "scripts": [ + "background.js" + ] + }, + "content_scripts": [ + { + "matches": [ "http://*/*", "https://*/*", "file:///*" ], + "js": ["content.js"], + "run_at": "document_end" + } + ], + "page_action": { + "default_icon": "icons/nostr.svg", + "default_title": "Nostr", + "default_popup": "popup/nostr-links.html" + } +} diff --git a/popup/nostr-links.css b/popup/nostr-links.css new file mode 100644 index 0000000..390cf72 --- /dev/null +++ b/popup/nostr-links.css @@ -0,0 +1,49 @@ +body { + margin: 0; + padding: 0; + font-family: -apple-system, BlinkMacSystemFont, sans-serif; + background-color: #fff; + min-width: 200px; + border: 1px solid #ccc; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); +} + +.dropdown-menu { + list-style: none; + margin: 0; + padding: 4px 0; +} + +.dropdown-menu li { + margin: 0; + padding: 0; +} + +.dropdown-menu a { + display: flex; + align-items: center; + padding: 8px 12px; + color: #333; + text-decoration: none; + white-space: nowrap; + transition: background-color 0.1s ease; +} + +.dropdown-menu a:hover { + background-color: #f0f0f0; + color: #000; +} + +.dropdown-menu a.disabled { + color: #999; + pointer-events: none; + cursor: default; +} + +.menu-icon { + display: none; + width: 16px; + height: 16px; + margin-right: 8px; + vertical-align: middle; +} diff --git a/popup/nostr-links.html b/popup/nostr-links.html new file mode 100644 index 0000000..c2e1cd6 --- /dev/null +++ b/popup/nostr-links.html @@ -0,0 +1,13 @@ + + + + + + Nostr Links + + + + + + + diff --git a/popup/nostr-links.js b/popup/nostr-links.js new file mode 100644 index 0000000..3feb3bb --- /dev/null +++ b/popup/nostr-links.js @@ -0,0 +1,20 @@ +browser.tabs.query({ active: true, currentWindow: true }, (tabs) => { + const tabId = tabs[0].id; + + browser.runtime.sendMessage({ action: "getNostrLinks", tabId }, (response) => { + const menu = document.getElementById("link-menu"); + if (response.links && response.links.length > 0) { + menu.innerHTML = response.links + .map( + (link) => ` +
  • + + ${link.text || "Open on Nostr"} + +
  • + ` + ) + .join(""); + } + }); +});