diff --git a/apps/web-clipper/.gitignore b/apps/web-clipper/.gitignore
index 77738287f0..3e99175bf8 100644
--- a/apps/web-clipper/.gitignore
+++ b/apps/web-clipper/.gitignore
@@ -1 +1,2 @@
-dist/
\ No newline at end of file
+.output
+.wxt
\ No newline at end of file
diff --git a/apps/web-clipper/.wxt/tsconfig.json b/apps/web-clipper/.wxt/tsconfig.json
deleted file mode 100644
index 0967ef424b..0000000000
--- a/apps/web-clipper/.wxt/tsconfig.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/apps/web-clipper/entrypoints/background/index.js b/apps/web-clipper/entrypoints/background/index.js
index 4074987abb..ee92f709a5 100644
--- a/apps/web-clipper/entrypoints/background/index.js
+++ b/apps/web-clipper/entrypoints/background/index.js
@@ -1,451 +1,453 @@
-// Keyboard shortcuts
-chrome.commands.onCommand.addListener(async function (command) {
- if (command == "saveSelection") {
- await saveSelection();
- } else if (command == "saveWholePage") {
- await saveWholePage();
- } else if (command == "saveTabs") {
- await saveTabs();
- } else if (command == "saveCroppedScreenshot") {
+export default defineBackground(() => {
+ // Keyboard shortcuts
+ chrome.commands.onCommand.addListener(async function (command) {
+ if (command == "saveSelection") {
+ await saveSelection();
+ } else if (command == "saveWholePage") {
+ await saveWholePage();
+ } else if (command == "saveTabs") {
+ await saveTabs();
+ } else if (command == "saveCroppedScreenshot") {
+ const activeTab = await getActiveTab();
+
+ await saveCroppedScreenshot(activeTab.url);
+ } else {
+ console.log("Unrecognized command", command);
+ }
+ });
+
+ function cropImage(newArea, dataUrl) {
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+
+ img.onload = function () {
+ const canvas = document.createElement('canvas');
+ canvas.width = newArea.width;
+ canvas.height = newArea.height;
+
+ const ctx = canvas.getContext('2d');
+
+ ctx.drawImage(img, newArea.x, newArea.y, newArea.width, newArea.height, 0, 0, newArea.width, newArea.height);
+
+ resolve(canvas.toDataURL());
+ };
+
+ img.src = dataUrl;
+ });
+ }
+
+ async function takeCroppedScreenshot(cropRect) {
+ const activeTab = await getActiveTab();
+ const zoom = await browser.tabs.getZoom(activeTab.id) * window.devicePixelRatio;
+
+ const newArea = Object.assign({}, cropRect);
+ newArea.x *= zoom;
+ newArea.y *= zoom;
+ newArea.width *= zoom;
+ newArea.height *= zoom;
+
+ const dataUrl = await browser.tabs.captureVisibleTab(null, { format: 'png' });
+
+ return await cropImage(newArea, dataUrl);
+ }
+
+ async function takeWholeScreenshot() {
+ // this saves only visible portion of the page
+ // workaround to save the whole page is to scroll & stitch
+ // example in https://github.com/mrcoles/full-page-screen-capture-chrome-extension
+ // see page.js and popup.js
+ return await browser.tabs.captureVisibleTab(null, { format: 'png' });
+ }
+
+ browser.runtime.onInstalled.addListener(() => {
+ if (isDevEnv()) {
+ browser.browserAction.setIcon({
+ path: 'icons/32-dev.png',
+ });
+ }
+ });
+
+ browser.contextMenus.create({
+ id: "trilium-save-selection",
+ title: "Save selection to Trilium",
+ contexts: ["selection"]
+ });
+
+ browser.contextMenus.create({
+ id: "trilium-save-cropped-screenshot",
+ title: "Clip screenshot to Trilium",
+ contexts: ["page"]
+ });
+
+ browser.contextMenus.create({
+ id: "trilium-save-cropped-screenshot",
+ title: "Crop screen shot to Trilium",
+ contexts: ["page"]
+ });
+
+ browser.contextMenus.create({
+ id: "trilium-save-whole-screenshot",
+ title: "Save whole screen shot to Trilium",
+ contexts: ["page"]
+ });
+
+ browser.contextMenus.create({
+ id: "trilium-save-page",
+ title: "Save whole page to Trilium",
+ contexts: ["page"]
+ });
+
+ browser.contextMenus.create({
+ id: "trilium-save-link",
+ title: "Save link to Trilium",
+ contexts: ["link"]
+ });
+
+ browser.contextMenus.create({
+ id: "trilium-save-image",
+ title: "Save image to Trilium",
+ contexts: ["image"]
+ });
+
+ async function getActiveTab() {
+ const tabs = await browser.tabs.query({
+ active: true,
+ currentWindow: true
+ });
+
+ return tabs[0];
+ }
+
+ async function getWindowTabs() {
+ const tabs = await browser.tabs.query({
+ currentWindow: true
+ });
+
+ return tabs;
+ }
+
+ async function sendMessageToActiveTab(message) {
const activeTab = await getActiveTab();
- await saveCroppedScreenshot(activeTab.url);
- } else {
- console.log("Unrecognized command", command);
+ if (!activeTab) {
+ throw new Error("No active tab.");
+ }
+
+ try {
+ return await browser.tabs.sendMessage(activeTab.id, message);
+ }
+ catch (e) {
+ throw e;
+ }
}
-});
-
-function cropImage(newArea, dataUrl) {
- return new Promise((resolve, reject) => {
- const img = new Image();
-
- img.onload = function () {
- const canvas = document.createElement('canvas');
- canvas.width = newArea.width;
- canvas.height = newArea.height;
-
- const ctx = canvas.getContext('2d');
-
- ctx.drawImage(img, newArea.x, newArea.y, newArea.width, newArea.height, 0, 0, newArea.width, newArea.height);
-
- resolve(canvas.toDataURL());
- };
-
- img.src = dataUrl;
- });
-}
-
-async function takeCroppedScreenshot(cropRect) {
- const activeTab = await getActiveTab();
- const zoom = await browser.tabs.getZoom(activeTab.id) * window.devicePixelRatio;
-
- const newArea = Object.assign({}, cropRect);
- newArea.x *= zoom;
- newArea.y *= zoom;
- newArea.width *= zoom;
- newArea.height *= zoom;
-
- const dataUrl = await browser.tabs.captureVisibleTab(null, { format: 'png' });
-
- return await cropImage(newArea, dataUrl);
-}
-
-async function takeWholeScreenshot() {
- // this saves only visible portion of the page
- // workaround to save the whole page is to scroll & stitch
- // example in https://github.com/mrcoles/full-page-screen-capture-chrome-extension
- // see page.js and popup.js
- return await browser.tabs.captureVisibleTab(null, { format: 'png' });
-}
-
-browser.runtime.onInstalled.addListener(() => {
- if (isDevEnv()) {
- browser.browserAction.setIcon({
- path: 'icons/32-dev.png',
- });
- }
-});
-
-browser.contextMenus.create({
- id: "trilium-save-selection",
- title: "Save selection to Trilium",
- contexts: ["selection"]
-});
-
-browser.contextMenus.create({
- id: "trilium-save-cropped-screenshot",
- title: "Clip screenshot to Trilium",
- contexts: ["page"]
-});
-
-browser.contextMenus.create({
- id: "trilium-save-cropped-screenshot",
- title: "Crop screen shot to Trilium",
- contexts: ["page"]
-});
-
-browser.contextMenus.create({
- id: "trilium-save-whole-screenshot",
- title: "Save whole screen shot to Trilium",
- contexts: ["page"]
-});
-
-browser.contextMenus.create({
- id: "trilium-save-page",
- title: "Save whole page to Trilium",
- contexts: ["page"]
-});
-
-browser.contextMenus.create({
- id: "trilium-save-link",
- title: "Save link to Trilium",
- contexts: ["link"]
-});
-
-browser.contextMenus.create({
- id: "trilium-save-image",
- title: "Save image to Trilium",
- contexts: ["image"]
-});
-
-async function getActiveTab() {
- const tabs = await browser.tabs.query({
- active: true,
- currentWindow: true
- });
-
- return tabs[0];
-}
-
-async function getWindowTabs() {
- const tabs = await browser.tabs.query({
- currentWindow: true
- });
-
- return tabs;
-}
-
-async function sendMessageToActiveTab(message) {
- const activeTab = await getActiveTab();
-
- if (!activeTab) {
- throw new Error("No active tab.");
- }
-
- try {
- return await browser.tabs.sendMessage(activeTab.id, message);
- }
- catch (e) {
- throw e;
- }
-}
-
-function toast(message, noteId = null, tabIds = null) {
- sendMessageToActiveTab({
- name: 'toast',
- message: message,
- noteId: noteId,
- tabIds: tabIds
- });
-}
-
-function blob2base64(blob) {
- return new Promise(resolve => {
- const reader = new FileReader();
- reader.onloadend = function() {
- resolve(reader.result);
- };
- reader.readAsDataURL(blob);
- });
-}
-
-async function fetchImage(url) {
- const resp = await fetch(url);
- const blob = await resp.blob();
-
- return await blob2base64(blob);
-}
-
-async function postProcessImage(image) {
- if (image.src.startsWith("data:image/")) {
- image.dataUrl = image.src;
- image.src = "inline." + image.src.substr(11, 3); // this should extract file type - png/jpg
- }
- else {
- try {
- image.dataUrl = await fetchImage(image.src, image);
- }
- catch (e) {
- console.log(`Cannot fetch image from ${image.src}`);
- }
- }
-}
-
-async function postProcessImages(resp) {
- if (resp.images) {
- for (const image of resp.images) {
- await postProcessImage(image);
- }
- }
-}
-
-async function saveSelection() {
- const payload = await sendMessageToActiveTab({name: 'trilium-save-selection'});
-
- await postProcessImages(payload);
-
- const resp = await triliumServerFacade.callService('POST', 'clippings', payload);
-
- if (!resp) {
- return;
- }
-
- toast("Selection has been saved to Trilium.", resp.noteId);
-}
-
-async function getImagePayloadFromSrc(src, pageUrl) {
- const image = {
- imageId: randomString(20),
- src: src
- };
-
- await postProcessImage(image);
-
- const activeTab = await getActiveTab();
-
- return {
- title: activeTab.title,
- content: `
`,
- images: [image],
- pageUrl: pageUrl
- };
-}
-
-async function saveCroppedScreenshot(pageUrl) {
- const cropRect = await sendMessageToActiveTab({name: 'trilium-get-rectangle-for-screenshot'});
-
- const src = await takeCroppedScreenshot(cropRect);
-
- const payload = await getImagePayloadFromSrc(src, pageUrl);
-
- const resp = await triliumServerFacade.callService("POST", "clippings", payload);
-
- if (!resp) {
- return;
- }
-
- toast("Screenshot has been saved to Trilium.", resp.noteId);
-}
-
-async function saveWholeScreenshot(pageUrl) {
- const src = await takeWholeScreenshot();
-
- const payload = await getImagePayloadFromSrc(src, pageUrl);
-
- const resp = await triliumServerFacade.callService("POST", "clippings", payload);
-
- if (!resp) {
- return;
- }
-
- toast("Screenshot has been saved to Trilium.", resp.noteId);
-}
-
-async function saveImage(srcUrl, pageUrl) {
- const payload = await getImagePayloadFromSrc(srcUrl, pageUrl);
-
- const resp = await triliumServerFacade.callService("POST", "clippings", payload);
-
- if (!resp) {
- return;
- }
-
- toast("Image has been saved to Trilium.", resp.noteId);
-}
-
-async function saveWholePage() {
- const payload = await sendMessageToActiveTab({name: 'trilium-save-page'});
-
- await postProcessImages(payload);
-
- const resp = await triliumServerFacade.callService('POST', 'notes', payload);
-
- if (!resp) {
- return;
- }
-
- toast("Page has been saved to Trilium.", resp.noteId);
-}
-
-async function saveLinkWithNote(title, content) {
- const activeTab = await getActiveTab();
-
- if (!title.trim()) {
- title = activeTab.title;
- }
-
- const resp = await triliumServerFacade.callService('POST', 'notes', {
- title: title,
- content: content,
- clipType: 'note',
- pageUrl: activeTab.url
- });
-
- if (!resp) {
- return false;
- }
-
- toast("Link with note has been saved to Trilium.", resp.noteId);
-
- return true;
-}
-
-async function getTabsPayload(tabs) {
- let content = '
';
- tabs.forEach(tab => {
- content += `- ${tab.title}
`
- });
- 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();
- }
- else if (info.menuItemId === 'trilium-save-cropped-screenshot') {
- await saveCroppedScreenshot(info.pageUrl);
- }
- else if (info.menuItemId === 'trilium-save-whole-screenshot') {
- await saveWholeScreenshot(info.pageUrl);
- }
- else if (info.menuItemId === 'trilium-save-image') {
- await saveImage(info.srcUrl, info.pageUrl);
- }
- else if (info.menuItemId === 'trilium-save-link') {
- const link = document.createElement("a");
- link.href = info.linkUrl;
- // linkText might be available only in firefox
- link.appendChild(document.createTextNode(info.linkText || info.linkUrl));
-
- const activeTab = await getActiveTab();
-
- const resp = await triliumServerFacade.callService('POST', 'clippings', {
- title: activeTab.title,
- content: link.outerHTML,
- pageUrl: info.pageUrl
- });
-
- if (!resp) {
- return;
- }
-
- toast("Link has been saved to Trilium.", resp.noteId);
- }
- else if (info.menuItemId === 'trilium-save-page') {
- await saveWholePage();
- }
- else {
- console.log("Unrecognized menuItemId", info.menuItemId);
- }
-});
-
-browser.runtime.onMessage.addListener(async request => {
- console.log("Received", request);
-
- if (request.name === 'openNoteInTrilium') {
- const resp = await triliumServerFacade.callService('POST', 'open/' + request.noteId);
-
- if (!resp) {
- return;
- }
-
- // desktop app is not available so we need to open in browser
- if (resp.result === 'open-in-browser') {
- const {triliumServerUrl} = await browser.storage.sync.get("triliumServerUrl");
-
- if (triliumServerUrl) {
- const noteUrl = triliumServerUrl + '/#' + request.noteId;
-
- console.log("Opening new tab in browser", noteUrl);
-
- browser.tabs.create({
- url: noteUrl
- });
- }
- else {
- console.error("triliumServerUrl not found in local storage.");
- }
- }
- }
- 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});
- }
- else if (request.name === 'save-cropped-screenshot') {
- const activeTab = await getActiveTab();
-
- return await saveCroppedScreenshot(activeTab.url);
- }
- else if (request.name === 'save-whole-screenshot') {
- const activeTab = await getActiveTab();
-
- return await saveWholeScreenshot(activeTab.url);
- }
- else if (request.name === 'save-whole-page') {
- return await saveWholePage();
- }
- else if (request.name === 'save-link-with-note') {
- return await saveLinkWithNote(request.title, request.content);
- }
- else if (request.name === 'save-tabs') {
- return await saveTabs();
- }
- else if (request.name === 'trigger-trilium-search') {
- triliumServerFacade.triggerSearchForTrilium();
- }
- else if (request.name === 'send-trilium-search-status') {
- triliumServerFacade.sendTriliumSearchStatusToPopup();
- }
- else if (request.name === 'trigger-trilium-search-note-url') {
- const activeTab = await getActiveTab();
- triliumServerFacade.triggerSearchNoteByUrl(activeTab.url);
- }
+
+ function toast(message, noteId = null, tabIds = null) {
+ sendMessageToActiveTab({
+ name: 'toast',
+ message: message,
+ noteId: noteId,
+ tabIds: tabIds
+ });
+ }
+
+ function blob2base64(blob) {
+ return new Promise(resolve => {
+ const reader = new FileReader();
+ reader.onloadend = function() {
+ resolve(reader.result);
+ };
+ reader.readAsDataURL(blob);
+ });
+ }
+
+ async function fetchImage(url) {
+ const resp = await fetch(url);
+ const blob = await resp.blob();
+
+ return await blob2base64(blob);
+ }
+
+ async function postProcessImage(image) {
+ if (image.src.startsWith("data:image/")) {
+ image.dataUrl = image.src;
+ image.src = "inline." + image.src.substr(11, 3); // this should extract file type - png/jpg
+ }
+ else {
+ try {
+ image.dataUrl = await fetchImage(image.src, image);
+ }
+ catch (e) {
+ console.log(`Cannot fetch image from ${image.src}`);
+ }
+ }
+ }
+
+ async function postProcessImages(resp) {
+ if (resp.images) {
+ for (const image of resp.images) {
+ await postProcessImage(image);
+ }
+ }
+ }
+
+ async function saveSelection() {
+ const payload = await sendMessageToActiveTab({name: 'trilium-save-selection'});
+
+ await postProcessImages(payload);
+
+ const resp = await triliumServerFacade.callService('POST', 'clippings', payload);
+
+ if (!resp) {
+ return;
+ }
+
+ toast("Selection has been saved to Trilium.", resp.noteId);
+ }
+
+ async function getImagePayloadFromSrc(src, pageUrl) {
+ const image = {
+ imageId: randomString(20),
+ src: src
+ };
+
+ await postProcessImage(image);
+
+ const activeTab = await getActiveTab();
+
+ return {
+ title: activeTab.title,
+ content: `
`,
+ images: [image],
+ pageUrl: pageUrl
+ };
+ }
+
+ async function saveCroppedScreenshot(pageUrl) {
+ const cropRect = await sendMessageToActiveTab({name: 'trilium-get-rectangle-for-screenshot'});
+
+ const src = await takeCroppedScreenshot(cropRect);
+
+ const payload = await getImagePayloadFromSrc(src, pageUrl);
+
+ const resp = await triliumServerFacade.callService("POST", "clippings", payload);
+
+ if (!resp) {
+ return;
+ }
+
+ toast("Screenshot has been saved to Trilium.", resp.noteId);
+ }
+
+ async function saveWholeScreenshot(pageUrl) {
+ const src = await takeWholeScreenshot();
+
+ const payload = await getImagePayloadFromSrc(src, pageUrl);
+
+ const resp = await triliumServerFacade.callService("POST", "clippings", payload);
+
+ if (!resp) {
+ return;
+ }
+
+ toast("Screenshot has been saved to Trilium.", resp.noteId);
+ }
+
+ async function saveImage(srcUrl, pageUrl) {
+ const payload = await getImagePayloadFromSrc(srcUrl, pageUrl);
+
+ const resp = await triliumServerFacade.callService("POST", "clippings", payload);
+
+ if (!resp) {
+ return;
+ }
+
+ toast("Image has been saved to Trilium.", resp.noteId);
+ }
+
+ async function saveWholePage() {
+ const payload = await sendMessageToActiveTab({name: 'trilium-save-page'});
+
+ await postProcessImages(payload);
+
+ const resp = await triliumServerFacade.callService('POST', 'notes', payload);
+
+ if (!resp) {
+ return;
+ }
+
+ toast("Page has been saved to Trilium.", resp.noteId);
+ }
+
+ async function saveLinkWithNote(title, content) {
+ const activeTab = await getActiveTab();
+
+ if (!title.trim()) {
+ title = activeTab.title;
+ }
+
+ const resp = await triliumServerFacade.callService('POST', 'notes', {
+ title: title,
+ content: content,
+ clipType: 'note',
+ pageUrl: activeTab.url
+ });
+
+ if (!resp) {
+ return false;
+ }
+
+ toast("Link with note has been saved to Trilium.", resp.noteId);
+
+ return true;
+ }
+
+ async function getTabsPayload(tabs) {
+ let content = '';
+ tabs.forEach(tab => {
+ content += `- ${tab.title}
`
+ });
+ 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();
+ }
+ else if (info.menuItemId === 'trilium-save-cropped-screenshot') {
+ await saveCroppedScreenshot(info.pageUrl);
+ }
+ else if (info.menuItemId === 'trilium-save-whole-screenshot') {
+ await saveWholeScreenshot(info.pageUrl);
+ }
+ else if (info.menuItemId === 'trilium-save-image') {
+ await saveImage(info.srcUrl, info.pageUrl);
+ }
+ else if (info.menuItemId === 'trilium-save-link') {
+ const link = document.createElement("a");
+ link.href = info.linkUrl;
+ // linkText might be available only in firefox
+ link.appendChild(document.createTextNode(info.linkText || info.linkUrl));
+
+ const activeTab = await getActiveTab();
+
+ const resp = await triliumServerFacade.callService('POST', 'clippings', {
+ title: activeTab.title,
+ content: link.outerHTML,
+ pageUrl: info.pageUrl
+ });
+
+ if (!resp) {
+ return;
+ }
+
+ toast("Link has been saved to Trilium.", resp.noteId);
+ }
+ else if (info.menuItemId === 'trilium-save-page') {
+ await saveWholePage();
+ }
+ else {
+ console.log("Unrecognized menuItemId", info.menuItemId);
+ }
+ });
+
+ browser.runtime.onMessage.addListener(async request => {
+ console.log("Received", request);
+
+ if (request.name === 'openNoteInTrilium') {
+ const resp = await triliumServerFacade.callService('POST', 'open/' + request.noteId);
+
+ if (!resp) {
+ return;
+ }
+
+ // desktop app is not available so we need to open in browser
+ if (resp.result === 'open-in-browser') {
+ const {triliumServerUrl} = await browser.storage.sync.get("triliumServerUrl");
+
+ if (triliumServerUrl) {
+ const noteUrl = triliumServerUrl + '/#' + request.noteId;
+
+ console.log("Opening new tab in browser", noteUrl);
+
+ browser.tabs.create({
+ url: noteUrl
+ });
+ }
+ else {
+ console.error("triliumServerUrl not found in local storage.");
+ }
+ }
+ }
+ 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});
+ }
+ else if (request.name === 'save-cropped-screenshot') {
+ const activeTab = await getActiveTab();
+
+ return await saveCroppedScreenshot(activeTab.url);
+ }
+ else if (request.name === 'save-whole-screenshot') {
+ const activeTab = await getActiveTab();
+
+ return await saveWholeScreenshot(activeTab.url);
+ }
+ else if (request.name === 'save-whole-page') {
+ return await saveWholePage();
+ }
+ else if (request.name === 'save-link-with-note') {
+ return await saveLinkWithNote(request.title, request.content);
+ }
+ else if (request.name === 'save-tabs') {
+ return await saveTabs();
+ }
+ else if (request.name === 'trigger-trilium-search') {
+ triliumServerFacade.triggerSearchForTrilium();
+ }
+ else if (request.name === 'send-trilium-search-status') {
+ triliumServerFacade.sendTriliumSearchStatusToPopup();
+ }
+ else if (request.name === 'trigger-trilium-search-note-url') {
+ const activeTab = await getActiveTab();
+ triliumServerFacade.triggerSearchNoteByUrl(activeTab.url);
+ }
+ });
});
diff --git a/apps/web-clipper/entrypoints/content/index.js b/apps/web-clipper/entrypoints/content/index.js
index faacfa5464..c6ba6c438c 100644
--- a/apps/web-clipper/entrypoints/content/index.js
+++ b/apps/web-clipper/entrypoints/content/index.js
@@ -1,351 +1,358 @@
-function absoluteUrl(url) {
- if (!url) {
- return url;
- }
-
- const protocol = url.toLowerCase().split(':')[0];
- if (['http', 'https', 'file'].indexOf(protocol) >= 0) {
- return url;
- }
-
- if (url.indexOf('//') === 0) {
- return location.protocol + url;
- } else if (url[0] === '/') {
- return location.protocol + '//' + location.host + url;
- } else {
- return getBaseUrl() + '/' + url;
- }
-}
-
-function pageTitle() {
- const titleElements = document.getElementsByTagName("title");
-
- return titleElements.length ? titleElements[0].text.trim() : document.title.trim();
-}
-
-function getReadableDocument() {
- // Readability directly change the passed document, so clone to preserve the original web page.
- const documentCopy = document.cloneNode(true);
- const readability = new Readability(documentCopy, {
- serializer: el => el // so that .content is returned as DOM element instead of HTML
- });
-
- const article = readability.parse();
-
- if (!article) {
- throw new Error('Could not parse HTML document with Readability');
- }
-
- return {
- title: article.title,
- body: article.content,
- }
-}
-
-function getDocumentDates() {
- var dates = {
- publishedDate: null,
- modifiedDate: null,
- };
-
- const articlePublishedTime = document.querySelector("meta[property='article:published_time']");
- if (articlePublishedTime && articlePublishedTime.getAttribute('content')) {
- dates.publishedDate = new Date(articlePublishedTime.getAttribute('content'));
- }
-
- const articleModifiedTime = document.querySelector("meta[property='article:modified_time']");
- if (articleModifiedTime && articleModifiedTime.getAttribute('content')) {
- dates.modifiedDate = new Date(articleModifiedTime.getAttribute('content'));
- }
-
- // TODO: if we didn't get dates from meta, then try to get them from JSON-LD
-
- return dates;
-}
-
-function getRectangleArea() {
- return new Promise((resolve, reject) => {
- const overlay = document.createElement('div');
- overlay.style.opacity = '0.6';
- overlay.style.background = 'black';
- overlay.style.width = '100%';
- overlay.style.height = '100%';
- overlay.style.zIndex = 99999999;
- overlay.style.top = 0;
- overlay.style.left = 0;
- overlay.style.position = 'fixed';
-
- document.body.appendChild(overlay);
-
- const messageComp = document.createElement('div');
-
- const messageCompWidth = 300;
- messageComp.setAttribute("tabindex", "0"); // so that it can be focused
- messageComp.style.position = 'fixed';
- messageComp.style.opacity = '0.95';
- messageComp.style.fontSize = '14px';
- messageComp.style.width = messageCompWidth + 'px';
- messageComp.style.maxWidth = messageCompWidth + 'px';
- messageComp.style.border = '1px solid black';
- messageComp.style.background = 'white';
- messageComp.style.color = 'black';
- messageComp.style.top = '10px';
- messageComp.style.textAlign = 'center';
- messageComp.style.padding = '10px';
- messageComp.style.left = Math.round(document.body.clientWidth / 2 - messageCompWidth / 2) + 'px';
- messageComp.style.zIndex = overlay.style.zIndex + 1;
-
- messageComp.textContent = 'Drag and release to capture a screenshot';
-
- document.body.appendChild(messageComp);
-
- const selection = document.createElement('div');
- selection.style.opacity = '0.5';
- selection.style.border = '1px solid red';
- selection.style.background = 'white';
- selection.style.border = '2px solid black';
- selection.style.zIndex = overlay.style.zIndex - 1;
- selection.style.top = 0;
- selection.style.left = 0;
- selection.style.position = 'fixed';
-
- document.body.appendChild(selection);
-
- messageComp.focus(); // we listen on keypresses on this element to cancel on escape
-
- let isDragging = false;
- let draggingStartPos = null;
- let selectionArea = {};
-
- function updateSelection() {
- selection.style.left = selectionArea.x + 'px';
- selection.style.top = selectionArea.y + 'px';
- selection.style.width = selectionArea.width + 'px';
- selection.style.height = selectionArea.height + 'px';
- }
-
- function setSelectionSizeFromMouse(event) {
- if (event.clientX < draggingStartPos.x) {
- selectionArea.x = event.clientX;
- }
-
- if (event.clientY < draggingStartPos.y) {
- selectionArea.y = event.clientY;
- }
-
- selectionArea.width = Math.max(1, Math.abs(event.clientX - draggingStartPos.x));
- selectionArea.height = Math.max(1, Math.abs(event.clientY - draggingStartPos.y));
- updateSelection();
- }
-
- function selection_mouseDown(event) {
- selectionArea = {x: event.clientX, y: event.clientY, width: 0, height: 0};
- draggingStartPos = {x: event.clientX, y: event.clientY};
- isDragging = true;
- updateSelection();
- }
-
- function selection_mouseMove(event) {
- if (!isDragging) return;
- setSelectionSizeFromMouse(event);
- }
-
- function removeOverlay() {
- isDragging = false;
-
- overlay.removeEventListener('mousedown', selection_mouseDown);
- overlay.removeEventListener('mousemove', selection_mouseMove);
- overlay.removeEventListener('mouseup', selection_mouseUp);
-
- document.body.removeChild(overlay);
- document.body.removeChild(selection);
- document.body.removeChild(messageComp);
- }
-
- function selection_mouseUp(event) {
- setSelectionSizeFromMouse(event);
-
- removeOverlay();
-
- console.info('selectionArea:', selectionArea);
-
- if (!selectionArea || !selectionArea.width || !selectionArea.height) {
- return;
- }
-
- // Need to wait a bit before taking the screenshot to make sure
- // the overlays have been removed and don't appear in the
- // screenshot. 10ms is not enough.
- setTimeout(() => resolve(selectionArea), 100);
- }
-
- function cancel(event) {
- if (event.key === "Escape") {
- removeOverlay();
- }
- }
-
- overlay.addEventListener('mousedown', selection_mouseDown);
- overlay.addEventListener('mousemove', selection_mouseMove);
- overlay.addEventListener('mouseup', selection_mouseUp);
- overlay.addEventListener('mouseup', selection_mouseUp);
- messageComp.addEventListener('keydown', cancel);
- });
-}
-
-function makeLinksAbsolute(container) {
- for (const link of container.getElementsByTagName('a')) {
- if (link.href) {
- link.href = absoluteUrl(link.href);
- }
- }
-}
-
-function getImages(container) {
- const images = [];
-
- for (const img of container.getElementsByTagName('img')) {
- if (!img.src) {
- continue;
- }
-
- const existingImage = images.find(image => image.src === img.src);
-
- if (existingImage) {
- img.src = existingImage.imageId;
- }
- else {
- const imageId = randomString(20);
-
- images.push({
- imageId: imageId,
- src: img.src
- });
-
- img.src = imageId;
- }
- }
-
- 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);
-
- if (message.name === "toast") {
- let messageText;
-
- if (message.noteId) {
- messageText = document.createElement('p');
- messageText.setAttribute("style", "padding: 0; margin: 0; font-size: larger;")
- messageText.appendChild(document.createTextNode(message.message + " "));
- messageText.appendChild(createLink(
- {name: 'openNoteInTrilium', noteId: message.noteId},
- "Open in Trilium."
- ));
-
- // 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;
- }
-
- await requireLib('/lib/toast.js');
-
- showToast(messageText, {
- settings: {
- duration: 7000
- }
- });
- }
- else if (message.name === "trilium-save-selection") {
- const container = document.createElement('div');
-
- const selection = window.getSelection();
-
- for (let i = 0; i < selection.rangeCount; i++) {
- const range = selection.getRangeAt(i);
-
- container.appendChild(range.cloneContents());
- }
-
- makeLinksAbsolute(container);
-
- const images = getImages(container);
-
- return {
- title: pageTitle(),
- content: container.innerHTML,
- images: images,
- pageUrl: getPageLocationOrigin() + location.pathname + location.search + location.hash
- };
-
- }
- else if (message.name === 'trilium-get-rectangle-for-screenshot') {
- return getRectangleArea();
- }
- else if (message.name === "trilium-save-page") {
- await requireLib("/lib/JSDOMParser.js");
- await requireLib("/lib/Readability.js");
- await requireLib("/lib/Readability-readerable.js");
-
- const {title, body} = getReadableDocument();
-
- makeLinksAbsolute(body);
-
- const images = getImages(body);
-
- var labels = {};
- const dates = getDocumentDates();
- if (dates.publishedDate) {
- labels['publishedDate'] = dates.publishedDate.toISOString().substring(0, 10);
- }
- if (dates.modifiedDate) {
- labels['modifiedDate'] = dates.publishedDate.toISOString().substring(0, 10);
- }
-
- return {
- title: title,
- content: body.innerHTML,
- images: images,
- pageUrl: getPageLocationOrigin() + location.pathname + location.search,
- clipType: 'page',
- labels: labels
- };
- }
- else {
- throw new Error('Unknown command: ' + JSON.stringify(message));
- }
-}
-
-browser.runtime.onMessage.addListener(prepareMessageResponse);
-
-const loadedLibs = [];
-
-async function requireLib(libPath) {
- if (!loadedLibs.includes(libPath)) {
- loadedLibs.push(libPath);
-
- await browser.runtime.sendMessage({name: 'load-script', file: libPath});
- }
-}
+export default defineContentScript({
+ matches: [
+ ""
+ ],
+ main: () => {
+ function absoluteUrl(url) {
+ if (!url) {
+ return url;
+ }
+
+ const protocol = url.toLowerCase().split(':')[0];
+ if (['http', 'https', 'file'].indexOf(protocol) >= 0) {
+ return url;
+ }
+
+ if (url.indexOf('//') === 0) {
+ return location.protocol + url;
+ } else if (url[0] === '/') {
+ return location.protocol + '//' + location.host + url;
+ } else {
+ return getBaseUrl() + '/' + url;
+ }
+ }
+
+ function pageTitle() {
+ const titleElements = document.getElementsByTagName("title");
+
+ return titleElements.length ? titleElements[0].text.trim() : document.title.trim();
+ }
+
+ function getReadableDocument() {
+ // Readability directly change the passed document, so clone to preserve the original web page.
+ const documentCopy = document.cloneNode(true);
+ const readability = new Readability(documentCopy, {
+ serializer: el => el // so that .content is returned as DOM element instead of HTML
+ });
+
+ const article = readability.parse();
+
+ if (!article) {
+ throw new Error('Could not parse HTML document with Readability');
+ }
+
+ return {
+ title: article.title,
+ body: article.content,
+ }
+ }
+
+ function getDocumentDates() {
+ var dates = {
+ publishedDate: null,
+ modifiedDate: null,
+ };
+
+ const articlePublishedTime = document.querySelector("meta[property='article:published_time']");
+ if (articlePublishedTime && articlePublishedTime.getAttribute('content')) {
+ dates.publishedDate = new Date(articlePublishedTime.getAttribute('content'));
+ }
+
+ const articleModifiedTime = document.querySelector("meta[property='article:modified_time']");
+ if (articleModifiedTime && articleModifiedTime.getAttribute('content')) {
+ dates.modifiedDate = new Date(articleModifiedTime.getAttribute('content'));
+ }
+
+ // TODO: if we didn't get dates from meta, then try to get them from JSON-LD
+
+ return dates;
+ }
+
+ function getRectangleArea() {
+ return new Promise((resolve, reject) => {
+ const overlay = document.createElement('div');
+ overlay.style.opacity = '0.6';
+ overlay.style.background = 'black';
+ overlay.style.width = '100%';
+ overlay.style.height = '100%';
+ overlay.style.zIndex = 99999999;
+ overlay.style.top = 0;
+ overlay.style.left = 0;
+ overlay.style.position = 'fixed';
+
+ document.body.appendChild(overlay);
+
+ const messageComp = document.createElement('div');
+
+ const messageCompWidth = 300;
+ messageComp.setAttribute("tabindex", "0"); // so that it can be focused
+ messageComp.style.position = 'fixed';
+ messageComp.style.opacity = '0.95';
+ messageComp.style.fontSize = '14px';
+ messageComp.style.width = messageCompWidth + 'px';
+ messageComp.style.maxWidth = messageCompWidth + 'px';
+ messageComp.style.border = '1px solid black';
+ messageComp.style.background = 'white';
+ messageComp.style.color = 'black';
+ messageComp.style.top = '10px';
+ messageComp.style.textAlign = 'center';
+ messageComp.style.padding = '10px';
+ messageComp.style.left = Math.round(document.body.clientWidth / 2 - messageCompWidth / 2) + 'px';
+ messageComp.style.zIndex = overlay.style.zIndex + 1;
+
+ messageComp.textContent = 'Drag and release to capture a screenshot';
+
+ document.body.appendChild(messageComp);
+
+ const selection = document.createElement('div');
+ selection.style.opacity = '0.5';
+ selection.style.border = '1px solid red';
+ selection.style.background = 'white';
+ selection.style.border = '2px solid black';
+ selection.style.zIndex = overlay.style.zIndex - 1;
+ selection.style.top = 0;
+ selection.style.left = 0;
+ selection.style.position = 'fixed';
+
+ document.body.appendChild(selection);
+
+ messageComp.focus(); // we listen on keypresses on this element to cancel on escape
+
+ let isDragging = false;
+ let draggingStartPos = null;
+ let selectionArea = {};
+
+ function updateSelection() {
+ selection.style.left = selectionArea.x + 'px';
+ selection.style.top = selectionArea.y + 'px';
+ selection.style.width = selectionArea.width + 'px';
+ selection.style.height = selectionArea.height + 'px';
+ }
+
+ function setSelectionSizeFromMouse(event) {
+ if (event.clientX < draggingStartPos.x) {
+ selectionArea.x = event.clientX;
+ }
+
+ if (event.clientY < draggingStartPos.y) {
+ selectionArea.y = event.clientY;
+ }
+
+ selectionArea.width = Math.max(1, Math.abs(event.clientX - draggingStartPos.x));
+ selectionArea.height = Math.max(1, Math.abs(event.clientY - draggingStartPos.y));
+ updateSelection();
+ }
+
+ function selection_mouseDown(event) {
+ selectionArea = {x: event.clientX, y: event.clientY, width: 0, height: 0};
+ draggingStartPos = {x: event.clientX, y: event.clientY};
+ isDragging = true;
+ updateSelection();
+ }
+
+ function selection_mouseMove(event) {
+ if (!isDragging) return;
+ setSelectionSizeFromMouse(event);
+ }
+
+ function removeOverlay() {
+ isDragging = false;
+
+ overlay.removeEventListener('mousedown', selection_mouseDown);
+ overlay.removeEventListener('mousemove', selection_mouseMove);
+ overlay.removeEventListener('mouseup', selection_mouseUp);
+
+ document.body.removeChild(overlay);
+ document.body.removeChild(selection);
+ document.body.removeChild(messageComp);
+ }
+
+ function selection_mouseUp(event) {
+ setSelectionSizeFromMouse(event);
+
+ removeOverlay();
+
+ console.info('selectionArea:', selectionArea);
+
+ if (!selectionArea || !selectionArea.width || !selectionArea.height) {
+ return;
+ }
+
+ // Need to wait a bit before taking the screenshot to make sure
+ // the overlays have been removed and don't appear in the
+ // screenshot. 10ms is not enough.
+ setTimeout(() => resolve(selectionArea), 100);
+ }
+
+ function cancel(event) {
+ if (event.key === "Escape") {
+ removeOverlay();
+ }
+ }
+
+ overlay.addEventListener('mousedown', selection_mouseDown);
+ overlay.addEventListener('mousemove', selection_mouseMove);
+ overlay.addEventListener('mouseup', selection_mouseUp);
+ overlay.addEventListener('mouseup', selection_mouseUp);
+ messageComp.addEventListener('keydown', cancel);
+ });
+ }
+
+ function makeLinksAbsolute(container) {
+ for (const link of container.getElementsByTagName('a')) {
+ if (link.href) {
+ link.href = absoluteUrl(link.href);
+ }
+ }
+ }
+
+ function getImages(container) {
+ const images = [];
+
+ for (const img of container.getElementsByTagName('img')) {
+ if (!img.src) {
+ continue;
+ }
+
+ const existingImage = images.find(image => image.src === img.src);
+
+ if (existingImage) {
+ img.src = existingImage.imageId;
+ }
+ else {
+ const imageId = randomString(20);
+
+ images.push({
+ imageId: imageId,
+ src: img.src
+ });
+
+ img.src = imageId;
+ }
+ }
+
+ 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);
+
+ if (message.name === "toast") {
+ let messageText;
+
+ if (message.noteId) {
+ messageText = document.createElement('p');
+ messageText.setAttribute("style", "padding: 0; margin: 0; font-size: larger;")
+ messageText.appendChild(document.createTextNode(message.message + " "));
+ messageText.appendChild(createLink(
+ {name: 'openNoteInTrilium', noteId: message.noteId},
+ "Open in Trilium."
+ ));
+
+ // 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;
+ }
+
+ await requireLib('/lib/toast.js');
+
+ showToast(messageText, {
+ settings: {
+ duration: 7000
+ }
+ });
+ }
+ else if (message.name === "trilium-save-selection") {
+ const container = document.createElement('div');
+
+ const selection = window.getSelection();
+
+ for (let i = 0; i < selection.rangeCount; i++) {
+ const range = selection.getRangeAt(i);
+
+ container.appendChild(range.cloneContents());
+ }
+
+ makeLinksAbsolute(container);
+
+ const images = getImages(container);
+
+ return {
+ title: pageTitle(),
+ content: container.innerHTML,
+ images: images,
+ pageUrl: getPageLocationOrigin() + location.pathname + location.search + location.hash
+ };
+
+ }
+ else if (message.name === 'trilium-get-rectangle-for-screenshot') {
+ return getRectangleArea();
+ }
+ else if (message.name === "trilium-save-page") {
+ await requireLib("/lib/JSDOMParser.js");
+ await requireLib("/lib/Readability.js");
+ await requireLib("/lib/Readability-readerable.js");
+
+ const {title, body} = getReadableDocument();
+
+ makeLinksAbsolute(body);
+
+ const images = getImages(body);
+
+ var labels = {};
+ const dates = getDocumentDates();
+ if (dates.publishedDate) {
+ labels['publishedDate'] = dates.publishedDate.toISOString().substring(0, 10);
+ }
+ if (dates.modifiedDate) {
+ labels['modifiedDate'] = dates.publishedDate.toISOString().substring(0, 10);
+ }
+
+ return {
+ title: title,
+ content: body.innerHTML,
+ images: images,
+ pageUrl: getPageLocationOrigin() + location.pathname + location.search,
+ clipType: 'page',
+ labels: labels
+ };
+ }
+ else {
+ throw new Error('Unknown command: ' + JSON.stringify(message));
+ }
+ }
+
+ const loadedLibs = [];
+
+ async function requireLib(libPath) {
+ if (!loadedLibs.includes(libPath)) {
+ loadedLibs.push(libPath);
+
+ await browser.runtime.sendMessage({name: 'load-script', file: libPath});
+ }
+ }
+
+ browser.runtime.onMessage.addListener(prepareMessageResponse);
+ }
+});
diff --git a/apps/web-clipper/manifest.json b/apps/web-clipper/manifest.json
index 8e0c1ba2bc..70da3d1b1b 100644
--- a/apps/web-clipper/manifest.json
+++ b/apps/web-clipper/manifest.json
@@ -26,9 +26,6 @@
},
"content_scripts": [
{
- "matches": [
- ""
- ],
"js": [
"lib/browser-polyfill.js",
"utils.js"