diff --git a/background.js b/background.js index 060d8db75..b4ac88bdf 100644 --- a/background.js +++ b/background.js @@ -4,6 +4,8 @@ chrome.commands.onCommand.addListener(async function (command) { await saveSelection(); } else if (command == "saveWholePage") { await saveWholePage(); + } else if (command == "saveTabs") { + await saveTabs(); } else if (command == "saveScreenshot") { const activeTab = await getActiveTab(); @@ -100,6 +102,14 @@ async function getActiveTab() { return tabs[0]; } +async function getWindowTabs() { + const tabs = await browser.tabs.query({ + currentWindow: true + }); + + return tabs; +} + async function sendMessageToActiveTab(message) { const activeTab = await getActiveTab(); @@ -115,11 +125,12 @@ async function sendMessageToActiveTab(message) { } } -function toast(message, noteId = null) { +function toast(message, noteId = null, tabIds = null) { sendMessageToActiveTab({ name: 'toast', message: message, - noteId: noteId + noteId: noteId, + tabIds: tabIds }); } @@ -253,6 +264,50 @@ async function saveNote(title, content) { return true; } +async function getTabsPayload(tabs) { + let content = ''; + + const domainsCount = tabs.map(tab => tab.url) + .reduce((acc, url) => { + const hostname = new URL(url).hostname + return acc.set(hostname, (acc.get(hostname) || 0) + 1) + }, new Map()); + + let topDomains = [...domainsCount] + .sort((a, b) => {return b[1]-a[1]}) + .slice(0,3) + .map(domain=>domain[0]) + .join(', ') + + if (tabs.length > 3) { topDomains += '...' } + + return { + title: `${tabs.length} browser tabs: ${topDomains}`, + content: content, + clipType: 'tabs' + }; +} + +async function saveTabs() { + const tabs = await getWindowTabs(); + + const payload = await getTabsPayload(tabs); + + const resp = await triliumServerFacade.callService('POST', 'notes', payload); + + if (!resp) { + return; + } + + const tabIds = tabs.map(tab=>{return tab.id}); + + toast(`${tabs.length} links have been saved to Trilium.`, resp.noteId, tabIds); +} + browser.contextMenus.onClicked.addListener(async function(info, tab) { if (info.menuItemId === 'trilium-save-selection') { await saveSelection(); @@ -319,6 +374,9 @@ browser.runtime.onMessage.addListener(async request => { } } } + else if (request.name === 'closeTabs') { + return await browser.tabs.remove(request.tabIds) + } else if (request.name === 'load-script') { return await browser.tabs.executeScript({file: request.file}); } @@ -333,6 +391,9 @@ browser.runtime.onMessage.addListener(async request => { else if (request.name === 'save-note') { return await saveNote(request.title, request.content); } + else if (request.name === 'save-tabs') { + return await saveTabs(); + } else if (request.name === 'trigger-trilium-search') { triliumServerFacade.triggerSearchForTrilium(); } diff --git a/content.js b/content.js index 1fb006636..25048685d 100644 --- a/content.js +++ b/content.js @@ -209,6 +209,18 @@ function getImages(container) { return images; } +function createLink(clickAction, text, color = "lightskyblue") { + const link = document.createElement('a'); + link.href = "javascript:"; + link.style.color = color; + link.appendChild(document.createTextNode(text)); + link.addEventListener("click", () => { + browser.runtime.sendMessage(null, clickAction) + }); + + return link +} + async function prepareMessageResponse(message) { console.info('Message: ' + message.name); @@ -216,21 +228,23 @@ async function prepareMessageResponse(message) { let messageText; if (message.noteId) { - messageText = document.createElement('span'); + messageText = document.createElement('p'); + messageText.setAttribute("style", "padding: 0; margin: 0") messageText.appendChild(document.createTextNode(message.message + " ")); + messageText.appendChild(createLink( + {name: 'openNoteInTrilium', noteId: message.noteId}, + "Open in Trilium." + )); - const link = document.createElement('a'); - link.href = "javascript:"; - link.style.color = "lightskyblue"; - link.appendChild(document.createTextNode("Open in Trilium.")); - link.addEventListener("click", () => { - browser.runtime.sendMessage(null, { - name: 'openNoteInTrilium', - noteId: message.noteId - }) - }); - - messageText.appendChild(link); + // only after saving tabs + if (message.tabIds) { + messageText.appendChild(document.createElement("br")); + messageText.appendChild(createLink( + {name: 'closeTabs', tabIds: message.tabIds}, + "Close saved tabs.", + "tomato" + )); + } } else { messageText = message.message; @@ -304,4 +318,4 @@ async function requireLib(libPath) { await browser.runtime.sendMessage({name: 'load-script', file: libPath}); } -} \ No newline at end of file +} diff --git a/popup/popup.html b/popup/popup.html index fe94eed29..3a85fd7a1 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -21,6 +21,7 @@ +
@@ -43,4 +44,4 @@ - \ No newline at end of file + diff --git a/popup/popup.js b/popup/popup.js index ff61ca423..0b5a008db 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -12,6 +12,7 @@ async function sendMessage(message) { const $showOptionsButton = $("#show-options-button"); const $clipScreenShotButton = $("#clip-screenshot-button"); const $saveWholePageButton = $("#save-whole-page-button"); +const $saveTabsButton = $("#save-tabs-button"); $showOptionsButton.on("click", () => browser.runtime.openOptionsPage()); @@ -19,6 +20,8 @@ $clipScreenShotButton.on("click", () => sendMessage({name: 'save-screenshot'})); $saveWholePageButton.on("click", () => sendMessage({name: 'save-whole-page'})); +$saveTabsButton.on("click", () => sendMessage({name: 'save-tabs'})); + const $createTextNoteWrapper = $("#create-text-note-wrapper"); const $textNote = $("#create-text-note-textarea"); @@ -141,4 +144,4 @@ $checkConnectionButton.on("click", () => { }) }); -$(() => browser.runtime.sendMessage({name: "send-trilium-search-status"})); \ No newline at end of file +$(() => browser.runtime.sendMessage({name: "send-trilium-search-status"}));