mirror of
https://github.com/zadam/trilium.git
synced 2025-12-05 15:04:24 +01:00
Reset web-clipper to origin/main baseline for MV3 migration
This commit is contained in:
parent
0c1de7e183
commit
a00ea2e91d
@ -4,14 +4,12 @@
|
|||||||
|
|
||||||
**Trilium is in maintenance mode and Web Clipper is not likely to get new releases.**
|
**Trilium is in maintenance mode and Web Clipper is not likely to get new releases.**
|
||||||
|
|
||||||
Trilium Web Clipper is a web browser extension which allows user to clip text, screenshots, whole pages and short notes and save them directly to [Trilium Notes](https://github.com/zadam/trilium).
|
Trilium Web Clipper is a web browser extension which allows user to clip text, screenshots, whole pages and short notes and save them directly to [Trilium Notes](https://github.com/zadam/trilium).
|
||||||
|
|
||||||
For more details, see the [wiki page](https://github.com/zadam/trilium/wiki/Web-clipper).
|
For more details, see the [wiki page](https://github.com/zadam/trilium/wiki/Web-clipper).
|
||||||
|
|
||||||
## Keyboard shortcuts
|
## Keyboard shortcuts
|
||||||
|
Keyboard shortcuts are available for most functions:
|
||||||
Keyboard shortcuts are available for most functions:
|
|
||||||
|
|
||||||
* Save selected text: `Ctrl+Shift+S` (Mac: `Cmd+Shift+S`)
|
* Save selected text: `Ctrl+Shift+S` (Mac: `Cmd+Shift+S`)
|
||||||
* Save whole page: `Alt+Shift+S` (Mac: `Opt+Shift+S`)
|
* Save whole page: `Alt+Shift+S` (Mac: `Opt+Shift+S`)
|
||||||
* Save screenshot: `Ctrl+Shift+E` (Mac: `Cmd+Shift+E`)
|
* Save screenshot: `Ctrl+Shift+E` (Mac: `Cmd+Shift+E`)
|
||||||
@ -23,5 +21,4 @@ To set custom shortcuts, follow the directions for your browser.
|
|||||||
**Chrome**: `chrome://extensions/shortcuts`
|
**Chrome**: `chrome://extensions/shortcuts`
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
Some parts of the code are based on the [Joplin Notes browser extension](https://github.com/laurent22/joplin/tree/master/Clipper).
|
Some parts of the code are based on the [Joplin Notes browser extension](https://github.com/laurent22/joplin/tree/master/Clipper).
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
// Import modules
|
|
||||||
import { randomString } from './utils.js';
|
|
||||||
import { triliumServerFacade } from './trilium_server_facade.js';
|
|
||||||
|
|
||||||
// Keyboard shortcuts
|
// Keyboard shortcuts
|
||||||
chrome.commands.onCommand.addListener(async function (command) {
|
chrome.commands.onCommand.addListener(async function (command) {
|
||||||
if (command == "saveSelection") {
|
if (command == "saveSelection") {
|
||||||
@ -12,6 +8,7 @@ chrome.commands.onCommand.addListener(async function (command) {
|
|||||||
await saveTabs();
|
await saveTabs();
|
||||||
} else if (command == "saveCroppedScreenshot") {
|
} else if (command == "saveCroppedScreenshot") {
|
||||||
const activeTab = await getActiveTab();
|
const activeTab = await getActiveTab();
|
||||||
|
|
||||||
await saveCroppedScreenshot(activeTab.url);
|
await saveCroppedScreenshot(activeTab.url);
|
||||||
} else {
|
} else {
|
||||||
console.log("Unrecognized command", command);
|
console.log("Unrecognized command", command);
|
||||||
@ -19,547 +16,436 @@ chrome.commands.onCommand.addListener(async function (command) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function cropImage(newArea, dataUrl) {
|
function cropImage(newArea, dataUrl) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
|
|
||||||
img.onload = function () {
|
img.onload = function () {
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.width = newArea.width;
|
canvas.width = newArea.width;
|
||||||
canvas.height = newArea.height;
|
canvas.height = newArea.height;
|
||||||
|
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
ctx.drawImage(img, newArea.x, newArea.y, newArea.width, newArea.height, 0, 0, newArea.width, newArea.height);
|
ctx.drawImage(img, newArea.x, newArea.y, newArea.width, newArea.height, 0, 0, newArea.width, newArea.height);
|
||||||
|
|
||||||
resolve(canvas.toDataURL());
|
resolve(canvas.toDataURL());
|
||||||
};
|
};
|
||||||
|
|
||||||
img.src = dataUrl;
|
img.src = dataUrl;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function takeCroppedScreenshot(cropRect) {
|
async function takeCroppedScreenshot(cropRect) {
|
||||||
const activeTab = await getActiveTab();
|
const activeTab = await getActiveTab();
|
||||||
const zoom = await chrome.tabs.getZoom(activeTab.id) * globalThis.devicePixelRatio || 1;
|
const zoom = await browser.tabs.getZoom(activeTab.id) * window.devicePixelRatio;
|
||||||
|
|
||||||
const newArea = Object.assign({}, cropRect);
|
const newArea = Object.assign({}, cropRect);
|
||||||
newArea.x *= zoom;
|
newArea.x *= zoom;
|
||||||
newArea.y *= zoom;
|
newArea.y *= zoom;
|
||||||
newArea.width *= zoom;
|
newArea.width *= zoom;
|
||||||
newArea.height *= zoom;
|
newArea.height *= zoom;
|
||||||
|
|
||||||
const dataUrl = await chrome.tabs.captureVisibleTab(null, { format: 'png' });
|
const dataUrl = await browser.tabs.captureVisibleTab(null, { format: 'png' });
|
||||||
|
|
||||||
return await cropImage(newArea, dataUrl);
|
return await cropImage(newArea, dataUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function takeWholeScreenshot() {
|
async function takeWholeScreenshot() {
|
||||||
// this saves only visible portion of the page
|
// this saves only visible portion of the page
|
||||||
// workaround to save the whole page is to scroll & stitch
|
// workaround to save the whole page is to scroll & stitch
|
||||||
// example in https://github.com/mrcoles/full-page-screen-capture-chrome-extension
|
// example in https://github.com/mrcoles/full-page-screen-capture-chrome-extension
|
||||||
// see page.js and popup.js
|
// see page.js and popup.js
|
||||||
return await chrome.tabs.captureVisibleTab(null, { format: 'png' });
|
return await browser.tabs.captureVisibleTab(null, { format: 'png' });
|
||||||
}
|
}
|
||||||
|
|
||||||
chrome.runtime.onInstalled.addListener(() => {
|
browser.runtime.onInstalled.addListener(() => {
|
||||||
if (isDevEnv()) {
|
if (isDevEnv()) {
|
||||||
chrome.action.setIcon({
|
browser.browserAction.setIcon({
|
||||||
path: 'icons/32-dev.png',
|
path: 'icons/32-dev.png',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Context menus
|
browser.contextMenus.create({
|
||||||
chrome.contextMenus.create({
|
id: "trilium-save-selection",
|
||||||
id: "trilium-save-selection",
|
title: "Save selection to Trilium",
|
||||||
title: "Save selection to Trilium",
|
contexts: ["selection"]
|
||||||
contexts: ["selection"]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
chrome.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: "trilium-save-cropped-screenshot",
|
id: "trilium-save-cropped-screenshot",
|
||||||
title: "Clip screenshot to Trilium",
|
title: "Clip screenshot to Trilium",
|
||||||
contexts: ["page"]
|
contexts: ["page"]
|
||||||
});
|
});
|
||||||
|
|
||||||
chrome.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: "trilium-save-whole-screenshot",
|
id: "trilium-save-cropped-screenshot",
|
||||||
title: "Save whole screen shot to Trilium",
|
title: "Crop screen shot to Trilium",
|
||||||
contexts: ["page"]
|
contexts: ["page"]
|
||||||
});
|
});
|
||||||
|
|
||||||
chrome.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: "trilium-save-page",
|
id: "trilium-save-whole-screenshot",
|
||||||
title: "Save whole page to Trilium",
|
title: "Save whole screen shot to Trilium",
|
||||||
contexts: ["page"]
|
contexts: ["page"]
|
||||||
});
|
});
|
||||||
|
|
||||||
chrome.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: "trilium-save-link",
|
id: "trilium-save-page",
|
||||||
title: "Save link to Trilium",
|
title: "Save whole page to Trilium",
|
||||||
contexts: ["link"]
|
contexts: ["page"]
|
||||||
});
|
});
|
||||||
|
|
||||||
chrome.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: "trilium-save-image",
|
id: "trilium-save-link",
|
||||||
title: "Save image to Trilium",
|
title: "Save link to Trilium",
|
||||||
contexts: ["image"]
|
contexts: ["link"]
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.contextMenus.create({
|
||||||
|
id: "trilium-save-image",
|
||||||
|
title: "Save image to Trilium",
|
||||||
|
contexts: ["image"]
|
||||||
});
|
});
|
||||||
|
|
||||||
async function getActiveTab() {
|
async function getActiveTab() {
|
||||||
const tabs = await chrome.tabs.query({
|
const tabs = await browser.tabs.query({
|
||||||
active: true,
|
active: true,
|
||||||
currentWindow: true
|
currentWindow: true
|
||||||
});
|
});
|
||||||
|
|
||||||
return tabs[0];
|
return tabs[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getWindowTabs() {
|
async function getWindowTabs() {
|
||||||
const tabs = await chrome.tabs.query({
|
const tabs = await browser.tabs.query({
|
||||||
currentWindow: true
|
currentWindow: true
|
||||||
});
|
});
|
||||||
|
|
||||||
return tabs;
|
return tabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendMessageToActiveTab(message) {
|
async function sendMessageToActiveTab(message) {
|
||||||
const activeTab = await getActiveTab();
|
const activeTab = await getActiveTab();
|
||||||
|
|
||||||
if (!activeTab) {
|
if (!activeTab) {
|
||||||
throw new Error("No active tab.");
|
throw new Error("No active tab.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// In Manifest V3, we need to inject content script if not already present
|
try {
|
||||||
try {
|
return await browser.tabs.sendMessage(activeTab.id, message);
|
||||||
return await chrome.tabs.sendMessage(activeTab.id, message);
|
}
|
||||||
} catch (error) {
|
catch (e) {
|
||||||
// Content script might not be injected, try to inject it
|
throw e;
|
||||||
try {
|
}
|
||||||
await chrome.scripting.executeScript({
|
|
||||||
target: { tabId: activeTab.id },
|
|
||||||
files: ['content.js']
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait a bit for the script to initialize
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 200));
|
|
||||||
|
|
||||||
return await chrome.tabs.sendMessage(activeTab.id, message);
|
|
||||||
} catch (injectionError) {
|
|
||||||
console.error('Failed to inject content script:', injectionError);
|
|
||||||
throw new Error(`Failed to communicate with page: ${injectionError.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toast(message, noteId = null, tabIds = null) {
|
function toast(message, noteId = null, tabIds = null) {
|
||||||
try {
|
sendMessageToActiveTab({
|
||||||
await sendMessageToActiveTab({
|
name: 'toast',
|
||||||
name: 'toast',
|
message: message,
|
||||||
message: message,
|
noteId: noteId,
|
||||||
noteId: noteId,
|
tabIds: tabIds
|
||||||
tabIds: tabIds
|
});
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to show toast:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showStatusToast(message, isProgress = true) {
|
|
||||||
// Make this completely async and fire-and-forget
|
|
||||||
// Only try to send status if we're confident the content script will be ready
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
// Test if content script is ready with a quick ping
|
|
||||||
const activeTab = await getActiveTab();
|
|
||||||
if (!activeTab) return;
|
|
||||||
|
|
||||||
await chrome.tabs.sendMessage(activeTab.id, { name: 'ping' });
|
|
||||||
// If ping succeeds, send the status toast
|
|
||||||
await chrome.tabs.sendMessage(activeTab.id, {
|
|
||||||
name: 'status-toast',
|
|
||||||
message: message,
|
|
||||||
isProgress: isProgress
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
// Content script not ready or failed - silently skip
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateStatusToast(message, isProgress = true) {
|
|
||||||
// Make this completely async and fire-and-forget
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
const activeTab = await getActiveTab();
|
|
||||||
if (!activeTab) return;
|
|
||||||
|
|
||||||
// Direct message without injection logic since content script should be ready by now
|
|
||||||
await chrome.tabs.sendMessage(activeTab.id, {
|
|
||||||
name: 'update-status-toast',
|
|
||||||
message: message,
|
|
||||||
isProgress: isProgress
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
// Content script not ready or failed - silently skip
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function blob2base64(blob) {
|
function blob2base64(blob) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onloadend = function() {
|
reader.onloadend = function() {
|
||||||
resolve(reader.result);
|
resolve(reader.result);
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(blob);
|
reader.readAsDataURL(blob);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchImage(url) {
|
async function fetchImage(url) {
|
||||||
const resp = await fetch(url);
|
const resp = await fetch(url);
|
||||||
const blob = await resp.blob();
|
const blob = await resp.blob();
|
||||||
|
|
||||||
return await blob2base64(blob);
|
return await blob2base64(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function postProcessImage(image) {
|
async function postProcessImage(image) {
|
||||||
if (image.src.startsWith("data:image/")) {
|
if (image.src.startsWith("data:image/")) {
|
||||||
image.dataUrl = image.src;
|
image.dataUrl = image.src;
|
||||||
image.src = "inline." + image.src.substr(11, 3); // this should extract file type - png/jpg
|
image.src = "inline." + image.src.substr(11, 3); // this should extract file type - png/jpg
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
image.dataUrl = await fetchImage(image.src, image);
|
image.dataUrl = await fetchImage(image.src, image);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.log(`Cannot fetch image from ${image.src}`);
|
console.log(`Cannot fetch image from ${image.src}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function postProcessImages(resp) {
|
async function postProcessImages(resp) {
|
||||||
if (resp && resp.images) {
|
if (resp.images) {
|
||||||
for (const image of resp.images) {
|
for (const image of resp.images) {
|
||||||
await postProcessImage(image);
|
await postProcessImage(image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveSelection() {
|
async function saveSelection() {
|
||||||
showStatusToast("📝 Capturing selection...");
|
const payload = await sendMessageToActiveTab({name: 'trilium-save-selection'});
|
||||||
|
|
||||||
const payload = await sendMessageToActiveTab({name: 'trilium-save-selection'});
|
await postProcessImages(payload);
|
||||||
|
|
||||||
if (!payload) {
|
const resp = await triliumServerFacade.callService('POST', 'clippings', payload);
|
||||||
console.error('No payload received from content script');
|
|
||||||
updateStatusToast("❌ Failed to capture selection", false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payload.images && payload.images.length > 0) {
|
if (!resp) {
|
||||||
updateStatusToast(`🖼️ Processing ${payload.images.length} image(s)...`);
|
return;
|
||||||
}
|
}
|
||||||
await postProcessImages(payload);
|
|
||||||
|
|
||||||
const triliumType = triliumServerFacade.triliumSearch?.status === 'found-desktop' ? 'Desktop' : 'Server';
|
toast("Selection has been saved to Trilium.", resp.noteId);
|
||||||
updateStatusToast(`💾 Saving to Trilium ${triliumType}...`);
|
|
||||||
|
|
||||||
const resp = await triliumServerFacade.callService('POST', 'clippings', payload);
|
|
||||||
|
|
||||||
if (!resp) {
|
|
||||||
updateStatusToast("❌ Failed to save to Trilium", false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await toast("✅ Selection has been saved to Trilium.", resp.noteId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getImagePayloadFromSrc(src, pageUrl) {
|
async function getImagePayloadFromSrc(src, pageUrl) {
|
||||||
const image = {
|
const image = {
|
||||||
imageId: randomString(20),
|
imageId: randomString(20),
|
||||||
src: src
|
src: src
|
||||||
};
|
};
|
||||||
|
|
||||||
await postProcessImage(image);
|
await postProcessImage(image);
|
||||||
|
|
||||||
const activeTab = await getActiveTab();
|
const activeTab = await getActiveTab();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: activeTab.title,
|
title: activeTab.title,
|
||||||
content: `<img src="${image.imageId}">`,
|
content: `<img src="${image.imageId}">`,
|
||||||
images: [image],
|
images: [image],
|
||||||
pageUrl: pageUrl
|
pageUrl: pageUrl
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveCroppedScreenshot(pageUrl) {
|
async function saveCroppedScreenshot(pageUrl) {
|
||||||
showStatusToast("📷 Preparing screenshot...");
|
const cropRect = await sendMessageToActiveTab({name: 'trilium-get-rectangle-for-screenshot'});
|
||||||
|
|
||||||
const cropRect = await sendMessageToActiveTab({name: 'trilium-get-rectangle-for-screenshot'});
|
const src = await takeCroppedScreenshot(cropRect);
|
||||||
|
|
||||||
updateStatusToast("📸 Capturing screenshot...");
|
const payload = await getImagePayloadFromSrc(src, pageUrl);
|
||||||
const src = await takeCroppedScreenshot(cropRect);
|
|
||||||
|
|
||||||
const payload = await getImagePayloadFromSrc(src, pageUrl);
|
const resp = await triliumServerFacade.callService("POST", "clippings", payload);
|
||||||
|
|
||||||
const triliumType = triliumServerFacade.triliumSearch?.status === 'found-desktop' ? 'Desktop' : 'Server';
|
if (!resp) {
|
||||||
updateStatusToast(`💾 Saving to Trilium ${triliumType}...`);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const resp = await triliumServerFacade.callService("POST", "clippings", payload);
|
toast("Screenshot has been saved to Trilium.", resp.noteId);
|
||||||
|
|
||||||
if (!resp) {
|
|
||||||
updateStatusToast("❌ Failed to save screenshot", false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await toast("✅ Screenshot has been saved to Trilium.", resp.noteId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveWholeScreenshot(pageUrl) {
|
async function saveWholeScreenshot(pageUrl) {
|
||||||
showStatusToast("📸 Capturing full screenshot...");
|
const src = await takeWholeScreenshot();
|
||||||
|
|
||||||
const src = await takeWholeScreenshot();
|
const payload = await getImagePayloadFromSrc(src, pageUrl);
|
||||||
|
|
||||||
const payload = await getImagePayloadFromSrc(src, pageUrl);
|
const resp = await triliumServerFacade.callService("POST", "clippings", payload);
|
||||||
|
|
||||||
const triliumType = triliumServerFacade.triliumSearch?.status === 'found-desktop' ? 'Desktop' : 'Server';
|
if (!resp) {
|
||||||
updateStatusToast(`💾 Saving to Trilium ${triliumType}...`);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const resp = await triliumServerFacade.callService("POST", "clippings", payload);
|
toast("Screenshot has been saved to Trilium.", resp.noteId);
|
||||||
|
|
||||||
if (!resp) {
|
|
||||||
updateStatusToast("❌ Failed to save screenshot", false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await toast("✅ Screenshot has been saved to Trilium.", resp.noteId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveImage(srcUrl, pageUrl) {
|
async function saveImage(srcUrl, pageUrl) {
|
||||||
const payload = await getImagePayloadFromSrc(srcUrl, pageUrl);
|
const payload = await getImagePayloadFromSrc(srcUrl, pageUrl);
|
||||||
|
|
||||||
const resp = await triliumServerFacade.callService("POST", "clippings", payload);
|
const resp = await triliumServerFacade.callService("POST", "clippings", payload);
|
||||||
|
|
||||||
if (!resp) {
|
if (!resp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await toast("Image has been saved to Trilium.", resp.noteId);
|
toast("Image has been saved to Trilium.", resp.noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveWholePage() {
|
async function saveWholePage() {
|
||||||
// Step 1: Show initial status (completely non-blocking)
|
const payload = await sendMessageToActiveTab({name: 'trilium-save-page'});
|
||||||
showStatusToast("📄 Page capture started...");
|
|
||||||
|
|
||||||
const payload = await sendMessageToActiveTab({name: 'trilium-save-page'});
|
await postProcessImages(payload);
|
||||||
|
|
||||||
if (!payload) {
|
const resp = await triliumServerFacade.callService('POST', 'notes', payload);
|
||||||
console.error('No payload received from content script');
|
|
||||||
updateStatusToast("❌ Failed to capture page content", false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2: Processing images
|
if (!resp) {
|
||||||
if (payload.images && payload.images.length > 0) {
|
return;
|
||||||
updateStatusToast(`🖼️ Processing ${payload.images.length} image(s)...`);
|
}
|
||||||
}
|
|
||||||
await postProcessImages(payload);
|
|
||||||
|
|
||||||
// Step 3: Saving to Trilium
|
toast("Page has been saved to Trilium.", resp.noteId);
|
||||||
const triliumType = triliumServerFacade.triliumSearch?.status === 'found-desktop' ? 'Desktop' : 'Server';
|
|
||||||
updateStatusToast(`💾 Saving to Trilium ${triliumType}...`);
|
|
||||||
|
|
||||||
const resp = await triliumServerFacade.callService('POST', 'notes', payload);
|
|
||||||
|
|
||||||
if (!resp) {
|
|
||||||
updateStatusToast("❌ Failed to save to Trilium", false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4: Success with link
|
|
||||||
await toast("✅ Page has been saved to Trilium.", resp.noteId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveLinkWithNote(title, content) {
|
async function saveLinkWithNote(title, content) {
|
||||||
const activeTab = await getActiveTab();
|
const activeTab = await getActiveTab();
|
||||||
|
|
||||||
if (!title.trim()) {
|
if (!title.trim()) {
|
||||||
title = activeTab.title;
|
title = activeTab.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resp = await triliumServerFacade.callService('POST', 'notes', {
|
const resp = await triliumServerFacade.callService('POST', 'notes', {
|
||||||
title: title,
|
title: title,
|
||||||
content: content,
|
content: content,
|
||||||
clipType: 'note',
|
clipType: 'note',
|
||||||
pageUrl: activeTab.url
|
pageUrl: activeTab.url
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!resp) {
|
if (!resp) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await toast("Link with note has been saved to Trilium.", resp.noteId);
|
toast("Link with note has been saved to Trilium.", resp.noteId);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getTabsPayload(tabs) {
|
async function getTabsPayload(tabs) {
|
||||||
let content = '<ul>';
|
let content = '<ul>';
|
||||||
tabs.forEach(tab => {
|
tabs.forEach(tab => {
|
||||||
content += `<li><a href="${tab.url}">${tab.title}</a></li>`
|
content += `<li><a href="${tab.url}">${tab.title}</a></li>`
|
||||||
});
|
});
|
||||||
content += '</ul>';
|
content += '</ul>';
|
||||||
|
|
||||||
const domainsCount = tabs.map(tab => tab.url)
|
const domainsCount = tabs.map(tab => tab.url)
|
||||||
.reduce((acc, url) => {
|
.reduce((acc, url) => {
|
||||||
const hostname = new URL(url).hostname
|
const hostname = new URL(url).hostname
|
||||||
return acc.set(hostname, (acc.get(hostname) || 0) + 1)
|
return acc.set(hostname, (acc.get(hostname) || 0) + 1)
|
||||||
}, new Map());
|
}, new Map());
|
||||||
|
|
||||||
let topDomains = [...domainsCount]
|
let topDomains = [...domainsCount]
|
||||||
.sort((a, b) => {return b[1]-a[1]})
|
.sort((a, b) => {return b[1]-a[1]})
|
||||||
.slice(0,3)
|
.slice(0,3)
|
||||||
.map(domain=>domain[0])
|
.map(domain=>domain[0])
|
||||||
.join(', ')
|
.join(', ')
|
||||||
|
|
||||||
if (tabs.length > 3) { topDomains += '...' }
|
if (tabs.length > 3) { topDomains += '...' }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: `${tabs.length} browser tabs: ${topDomains}`,
|
title: `${tabs.length} browser tabs: ${topDomains}`,
|
||||||
content: content,
|
content: content,
|
||||||
clipType: 'tabs'
|
clipType: 'tabs'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveTabs() {
|
async function saveTabs() {
|
||||||
const tabs = await getWindowTabs();
|
const tabs = await getWindowTabs();
|
||||||
|
|
||||||
const payload = await getTabsPayload(tabs);
|
const payload = await getTabsPayload(tabs);
|
||||||
|
|
||||||
const resp = await triliumServerFacade.callService('POST', 'notes', payload);
|
const resp = await triliumServerFacade.callService('POST', 'notes', payload);
|
||||||
|
|
||||||
if (!resp) {
|
if (!resp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabIds = tabs.map(tab=>{return tab.id});
|
const tabIds = tabs.map(tab=>{return tab.id});
|
||||||
|
|
||||||
await toast(`${tabs.length} links have been saved to Trilium.`, resp.noteId, tabIds);
|
toast(`${tabs.length} links have been saved to Trilium.`, resp.noteId, tabIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function
|
browser.contextMenus.onClicked.addListener(async function(info, tab) {
|
||||||
function isDevEnv() {
|
if (info.menuItemId === 'trilium-save-selection') {
|
||||||
const manifest = chrome.runtime.getManifest();
|
await saveSelection();
|
||||||
return manifest.name.endsWith('(dev)');
|
}
|
||||||
}
|
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));
|
||||||
|
|
||||||
chrome.contextMenus.onClicked.addListener(async function(info, tab) {
|
const activeTab = await getActiveTab();
|
||||||
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
|
||||||
|
});
|
||||||
|
|
||||||
const resp = await triliumServerFacade.callService('POST', 'clippings', {
|
if (!resp) {
|
||||||
title: activeTab.title,
|
return;
|
||||||
content: link.outerHTML,
|
}
|
||||||
pageUrl: info.pageUrl
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!resp) {
|
toast("Link has been saved to Trilium.", resp.noteId);
|
||||||
return;
|
}
|
||||||
}
|
else if (info.menuItemId === 'trilium-save-page') {
|
||||||
|
await saveWholePage();
|
||||||
await toast("Link has been saved to Trilium.", resp.noteId);
|
}
|
||||||
}
|
else {
|
||||||
else if (info.menuItemId === 'trilium-save-page') {
|
console.log("Unrecognized menuItemId", info.menuItemId);
|
||||||
await saveWholePage();
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("Unrecognized menuItemId", info.menuItemId);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
|
browser.runtime.onMessage.addListener(async request => {
|
||||||
console.log("Received", request);
|
console.log("Received", request);
|
||||||
|
|
||||||
if (request.name === 'openNoteInTrilium') {
|
if (request.name === 'openNoteInTrilium') {
|
||||||
const resp = await triliumServerFacade.callService('POST', 'open/' + request.noteId);
|
const resp = await triliumServerFacade.callService('POST', 'open/' + request.noteId);
|
||||||
|
|
||||||
if (!resp) {
|
if (!resp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// desktop app is not available so we need to open in browser
|
// desktop app is not available so we need to open in browser
|
||||||
if (resp.result === 'open-in-browser') {
|
if (resp.result === 'open-in-browser') {
|
||||||
const {triliumServerUrl} = await chrome.storage.sync.get("triliumServerUrl");
|
const {triliumServerUrl} = await browser.storage.sync.get("triliumServerUrl");
|
||||||
|
|
||||||
if (triliumServerUrl) {
|
if (triliumServerUrl) {
|
||||||
const noteUrl = triliumServerUrl + '/#' + request.noteId;
|
const noteUrl = triliumServerUrl + '/#' + request.noteId;
|
||||||
|
|
||||||
console.log("Opening new tab in browser", noteUrl);
|
console.log("Opening new tab in browser", noteUrl);
|
||||||
|
|
||||||
chrome.tabs.create({
|
browser.tabs.create({
|
||||||
url: noteUrl
|
url: noteUrl
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.error("triliumServerUrl not found in local storage.");
|
console.error("triliumServerUrl not found in local storage.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (request.name === 'closeTabs') {
|
else if (request.name === 'closeTabs') {
|
||||||
return await chrome.tabs.remove(request.tabIds)
|
return await browser.tabs.remove(request.tabIds)
|
||||||
}
|
}
|
||||||
else if (request.name === 'load-script') {
|
else if (request.name === 'load-script') {
|
||||||
return await chrome.scripting.executeScript({
|
return await browser.tabs.executeScript({file: request.file});
|
||||||
target: { tabId: sender.tab?.id },
|
}
|
||||||
files: [request.file]
|
else if (request.name === 'save-cropped-screenshot') {
|
||||||
});
|
const activeTab = await getActiveTab();
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Important: return true to indicate async response
|
return await saveCroppedScreenshot(activeTab.url);
|
||||||
return true;
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,33 +1,3 @@
|
|||||||
// Utility functions (inline to avoid module dependency issues)
|
|
||||||
function randomString(len) {
|
|
||||||
let text = "";
|
|
||||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBaseUrl() {
|
|
||||||
let output = getPageLocationOrigin() + location.pathname;
|
|
||||||
|
|
||||||
if (output[output.length - 1] !== '/') {
|
|
||||||
output = output.split('/');
|
|
||||||
output.pop();
|
|
||||||
output = output.join('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPageLocationOrigin() {
|
|
||||||
// location.origin normally returns the protocol + domain + port (eg. https://example.com:8080)
|
|
||||||
// but for file:// protocol this is browser dependant and in particular Firefox returns "null" in this case.
|
|
||||||
return location.protocol === 'file:' ? 'file://' : location.origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
function absoluteUrl(url) {
|
function absoluteUrl(url) {
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return url;
|
return url;
|
||||||
@ -75,19 +45,19 @@ function getReadableDocument() {
|
|||||||
function getDocumentDates() {
|
function getDocumentDates() {
|
||||||
var dates = {
|
var dates = {
|
||||||
publishedDate: null,
|
publishedDate: null,
|
||||||
modifiedDate: null,
|
modifiedDate: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const articlePublishedTime = document.querySelector("meta[property='article:published_time']");
|
const articlePublishedTime = document.querySelector("meta[property='article:published_time']");
|
||||||
if (articlePublishedTime && articlePublishedTime.getAttribute('content')) {
|
if (articlePublishedTime && articlePublishedTime.getAttribute('content')) {
|
||||||
dates.publishedDate = new Date(articlePublishedTime.getAttribute('content'));
|
dates.publishedDate = new Date(articlePublishedTime.getAttribute('content'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const articleModifiedTime = document.querySelector("meta[property='article:modified_time']");
|
const articleModifiedTime = document.querySelector("meta[property='article:modified_time']");
|
||||||
if (articleModifiedTime && articleModifiedTime.getAttribute('content')) {
|
if (articleModifiedTime && articleModifiedTime.getAttribute('content')) {
|
||||||
dates.modifiedDate = new Date(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
|
// TODO: if we didn't get dates from meta, then try to get them from JSON-LD
|
||||||
|
|
||||||
return dates;
|
return dates;
|
||||||
@ -265,7 +235,7 @@ function createLink(clickAction, text, color = "lightskyblue") {
|
|||||||
link.style.color = color;
|
link.style.color = color;
|
||||||
link.appendChild(document.createTextNode(text));
|
link.appendChild(document.createTextNode(text));
|
||||||
link.addEventListener("click", () => {
|
link.addEventListener("click", () => {
|
||||||
chrome.runtime.sendMessage(null, clickAction)
|
browser.runtime.sendMessage(null, clickAction)
|
||||||
});
|
});
|
||||||
|
|
||||||
return link
|
return link
|
||||||
@ -274,10 +244,7 @@ function createLink(clickAction, text, color = "lightskyblue") {
|
|||||||
async function prepareMessageResponse(message) {
|
async function prepareMessageResponse(message) {
|
||||||
console.info('Message: ' + message.name);
|
console.info('Message: ' + message.name);
|
||||||
|
|
||||||
if (message.name === "ping") {
|
if (message.name === "toast") {
|
||||||
return { success: true };
|
|
||||||
}
|
|
||||||
else if (message.name === "toast") {
|
|
||||||
let messageText;
|
let messageText;
|
||||||
|
|
||||||
if (message.noteId) {
|
if (message.noteId) {
|
||||||
@ -310,42 +277,6 @@ async function prepareMessageResponse(message) {
|
|||||||
duration: 7000
|
duration: 7000
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { success: true }; // Return a response
|
|
||||||
}
|
|
||||||
else if (message.name === "status-toast") {
|
|
||||||
await requireLib('/lib/toast.js');
|
|
||||||
|
|
||||||
// Hide any existing status toast
|
|
||||||
if (window.triliumStatusToast && window.triliumStatusToast.hide) {
|
|
||||||
window.triliumStatusToast.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store reference to the status toast so we can replace it
|
|
||||||
window.triliumStatusToast = showToast(message.message, {
|
|
||||||
settings: {
|
|
||||||
duration: message.isProgress ? 60000 : 5000 // Long duration for progress, shorter for errors
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return { success: true }; // Return a response
|
|
||||||
}
|
|
||||||
else if (message.name === "update-status-toast") {
|
|
||||||
await requireLib('/lib/toast.js');
|
|
||||||
|
|
||||||
// Hide the previous status toast
|
|
||||||
if (window.triliumStatusToast && window.triliumStatusToast.hide) {
|
|
||||||
window.triliumStatusToast.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show new toast with updated message
|
|
||||||
window.triliumStatusToast = showToast(message.message, {
|
|
||||||
settings: {
|
|
||||||
duration: message.isProgress ? 60000 : 5000
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return { success: true }; // Return a response
|
|
||||||
}
|
}
|
||||||
else if (message.name === "trilium-save-selection") {
|
else if (message.name === "trilium-save-selection") {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
@ -407,10 +338,7 @@ async function prepareMessageResponse(message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
browser.runtime.onMessage.addListener(prepareMessageResponse);
|
||||||
prepareMessageResponse(message).then(sendResponse);
|
|
||||||
return true; // Important: indicates async response
|
|
||||||
});
|
|
||||||
|
|
||||||
const loadedLibs = [];
|
const loadedLibs = [];
|
||||||
|
|
||||||
@ -418,6 +346,6 @@ async function requireLib(libPath) {
|
|||||||
if (!loadedLibs.includes(libPath)) {
|
if (!loadedLibs.includes(libPath)) {
|
||||||
loadedLibs.push(libPath);
|
loadedLibs.push(libPath);
|
||||||
|
|
||||||
await chrome.runtime.sendMessage({name: 'load-script', file: libPath});
|
await browser.runtime.sendMessage({name: 'load-script', file: libPath});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 2,
|
||||||
"name": "Trilium Web Clipper (dev)",
|
"name": "Trilium Web Clipper (dev)",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"description": "Save web clippings to Trilium Notes.",
|
"description": "Save web clippings to Trilium Notes.",
|
||||||
"homepage_url": "https://github.com/zadam/trilium-web-clipper",
|
"homepage_url": "https://github.com/zadam/trilium-web-clipper",
|
||||||
"content_security_policy": {
|
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
|
||||||
"extension_pages": "script-src 'self'; object-src 'self'"
|
|
||||||
},
|
|
||||||
"icons": {
|
"icons": {
|
||||||
"32": "icons/32.png",
|
"32": "icons/32.png",
|
||||||
"48": "icons/48.png",
|
"48": "icons/48.png",
|
||||||
@ -15,30 +13,37 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
"activeTab",
|
"activeTab",
|
||||||
"tabs",
|
"tabs",
|
||||||
"storage",
|
|
||||||
"contextMenus",
|
|
||||||
"scripting"
|
|
||||||
],
|
|
||||||
"host_permissions": [
|
|
||||||
"http://*/",
|
"http://*/",
|
||||||
"https://*/"
|
"https://*/",
|
||||||
|
"<all_urls>",
|
||||||
|
"storage",
|
||||||
|
"contextMenus"
|
||||||
],
|
],
|
||||||
"action": {
|
"browser_action": {
|
||||||
"default_icon": "icons/32.png",
|
"default_icon": "icons/32.png",
|
||||||
"default_title": "Trilium Web Clipper",
|
"default_title": "Trilium Web Clipper",
|
||||||
"default_popup": "popup/popup.html"
|
"default_popup": "popup/popup.html"
|
||||||
},
|
},
|
||||||
"content_scripts": [],
|
"content_scripts": [
|
||||||
"background": {
|
|
||||||
"service_worker": "background.js",
|
|
||||||
"type": "module"
|
|
||||||
},
|
|
||||||
"web_accessible_resources": [
|
|
||||||
{
|
{
|
||||||
"resources": ["lib/*", "utils.js", "trilium_server_facade.js", "content.js"],
|
"matches": [
|
||||||
"matches": ["<all_urls>"]
|
"<all_urls>"
|
||||||
|
],
|
||||||
|
"js": [
|
||||||
|
"lib/browser-polyfill.js",
|
||||||
|
"utils.js",
|
||||||
|
"content.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"background": {
|
||||||
|
"scripts": [
|
||||||
|
"lib/browser-polyfill.js",
|
||||||
|
"utils.js",
|
||||||
|
"trilium_server_facade.js",
|
||||||
|
"background.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
"options_ui": {
|
"options_ui": {
|
||||||
"page": "options/options.html"
|
"page": "options/options.html"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -56,7 +56,7 @@ async function saveTriliumServerSetup(e) {
|
|||||||
|
|
||||||
$triliumServerPassword.val('');
|
$triliumServerPassword.val('');
|
||||||
|
|
||||||
chrome.storage.sync.set({
|
browser.storage.sync.set({
|
||||||
triliumServerUrl: $triliumServerUrl.val(),
|
triliumServerUrl: $triliumServerUrl.val(),
|
||||||
authToken: json.token
|
authToken: json.token
|
||||||
});
|
});
|
||||||
@ -73,7 +73,7 @@ const $resetTriliumServerSetupLink = $("#reset-trilium-server-setup");
|
|||||||
$resetTriliumServerSetupLink.on("click", e => {
|
$resetTriliumServerSetupLink.on("click", e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
chrome.storage.sync.set({
|
browser.storage.sync.set({
|
||||||
triliumServerUrl: '',
|
triliumServerUrl: '',
|
||||||
authToken: ''
|
authToken: ''
|
||||||
});
|
});
|
||||||
@ -97,7 +97,7 @@ $triilumDesktopSetupForm.on("submit", e => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
chrome.storage.sync.set({
|
browser.storage.sync.set({
|
||||||
triliumDesktopPort: port
|
triliumDesktopPort: port
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -105,8 +105,8 @@ $triilumDesktopSetupForm.on("submit", e => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function restoreOptions() {
|
async function restoreOptions() {
|
||||||
const {triliumServerUrl} = await chrome.storage.sync.get("triliumServerUrl");
|
const {triliumServerUrl} = await browser.storage.sync.get("triliumServerUrl");
|
||||||
const {authToken} = await chrome.storage.sync.get("authToken");
|
const {authToken} = await browser.storage.sync.get("authToken");
|
||||||
|
|
||||||
$errorMessage.hide();
|
$errorMessage.hide();
|
||||||
$successMessage.hide();
|
$successMessage.hide();
|
||||||
@ -127,7 +127,7 @@ async function restoreOptions() {
|
|||||||
$triliumServerConfiguredDiv.hide();
|
$triliumServerConfiguredDiv.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
const {triliumDesktopPort} = await chrome.storage.sync.get("triliumDesktopPort");
|
const {triliumDesktopPort} = await browser.storage.sync.get("triliumDesktopPort");
|
||||||
|
|
||||||
$triliumDesktopPort.val(triliumDesktopPort);
|
$triliumDesktopPort.val(triliumDesktopPort);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
async function sendMessage(message) {
|
async function sendMessage(message) {
|
||||||
try {
|
try {
|
||||||
return await chrome.runtime.sendMessage(message);
|
return await browser.runtime.sendMessage(message);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.log("Calling browser runtime failed:", e);
|
console.log("Calling browser runtime failed:", e);
|
||||||
@ -15,7 +15,7 @@ const $saveWholeScreenShotButton = $("#save-whole-screenshot-button");
|
|||||||
const $saveWholePageButton = $("#save-whole-page-button");
|
const $saveWholePageButton = $("#save-whole-page-button");
|
||||||
const $saveTabsButton = $("#save-tabs-button");
|
const $saveTabsButton = $("#save-tabs-button");
|
||||||
|
|
||||||
$showOptionsButton.on("click", () => chrome.runtime.openOptionsPage());
|
$showOptionsButton.on("click", () => browser.runtime.openOptionsPage());
|
||||||
|
|
||||||
$saveCroppedScreenShotButton.on("click", () => {
|
$saveCroppedScreenShotButton.on("click", () => {
|
||||||
sendMessage({name: 'save-cropped-screenshot'});
|
sendMessage({name: 'save-cropped-screenshot'});
|
||||||
@ -115,7 +115,7 @@ const $connectionStatus = $("#connection-status");
|
|||||||
const $needsConnection = $(".needs-connection");
|
const $needsConnection = $(".needs-connection");
|
||||||
const $alreadyVisited = $("#already-visited");
|
const $alreadyVisited = $("#already-visited");
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener(request => {
|
browser.runtime.onMessage.addListener(request => {
|
||||||
if (request.name === 'trilium-search-status') {
|
if (request.name === 'trilium-search-status') {
|
||||||
const {triliumSearch} = request;
|
const {triliumSearch} = request;
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ chrome.runtime.onMessage.addListener(request => {
|
|||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
$needsConnection.removeAttr("disabled");
|
$needsConnection.removeAttr("disabled");
|
||||||
$needsConnection.removeAttr("title");
|
$needsConnection.removeAttr("title");
|
||||||
chrome.runtime.sendMessage({name: "trigger-trilium-search-note-url"});
|
browser.runtime.sendMessage({name: "trigger-trilium-search-note-url"});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$needsConnection.attr("disabled", "disabled");
|
$needsConnection.attr("disabled", "disabled");
|
||||||
@ -164,7 +164,7 @@ chrome.runtime.onMessage.addListener(request => {
|
|||||||
}else{
|
}else{
|
||||||
$alreadyVisited.html('');
|
$alreadyVisited.html('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -172,9 +172,9 @@ chrome.runtime.onMessage.addListener(request => {
|
|||||||
const $checkConnectionButton = $("#check-connection-button");
|
const $checkConnectionButton = $("#check-connection-button");
|
||||||
|
|
||||||
$checkConnectionButton.on("click", () => {
|
$checkConnectionButton.on("click", () => {
|
||||||
chrome.runtime.sendMessage({
|
browser.runtime.sendMessage({
|
||||||
name: "trigger-trilium-search"
|
name: "trigger-trilium-search"
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
$(() => chrome.runtime.sendMessage({name: "send-trilium-search-status"}));
|
$(() => browser.runtime.sendMessage({name: "send-trilium-search-status"}));
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
const PROTOCOL_VERSION_MAJOR = 1;
|
const PROTOCOL_VERSION_MAJOR = 1;
|
||||||
|
|
||||||
function isDevEnv() {
|
function isDevEnv() {
|
||||||
const manifest = chrome.runtime.getManifest();
|
const manifest = browser.runtime.getManifest();
|
||||||
|
|
||||||
return manifest.name.endsWith('(dev)');
|
return manifest.name.endsWith('(dev)');
|
||||||
}
|
}
|
||||||
@ -16,7 +16,7 @@ class TriliumServerFacade {
|
|||||||
|
|
||||||
async sendTriliumSearchStatusToPopup() {
|
async sendTriliumSearchStatusToPopup() {
|
||||||
try {
|
try {
|
||||||
await chrome.runtime.sendMessage({
|
await browser.runtime.sendMessage({
|
||||||
name: "trilium-search-status",
|
name: "trilium-search-status",
|
||||||
triliumSearch: this.triliumSearch
|
triliumSearch: this.triliumSearch
|
||||||
});
|
});
|
||||||
@ -25,7 +25,7 @@ class TriliumServerFacade {
|
|||||||
}
|
}
|
||||||
async sendTriliumSearchNoteToPopup(){
|
async sendTriliumSearchNoteToPopup(){
|
||||||
try{
|
try{
|
||||||
await chrome.runtime.sendMessage({
|
await browser.runtime.sendMessage({
|
||||||
name: "trilium-previously-visited",
|
name: "trilium-previously-visited",
|
||||||
searchNote: this.triliumSearchNote
|
searchNote: this.triliumSearchNote
|
||||||
})
|
})
|
||||||
@ -95,8 +95,8 @@ class TriliumServerFacade {
|
|||||||
// continue
|
// continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const {triliumServerUrl} = await chrome.storage.sync.get("triliumServerUrl");
|
const {triliumServerUrl} = await browser.storage.sync.get("triliumServerUrl");
|
||||||
const {authToken} = await chrome.storage.sync.get("authToken");
|
const {authToken} = await browser.storage.sync.get("authToken");
|
||||||
|
|
||||||
if (triliumServerUrl && authToken) {
|
if (triliumServerUrl && authToken) {
|
||||||
try {
|
try {
|
||||||
@ -162,7 +162,7 @@ class TriliumServerFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getPort() {
|
async getPort() {
|
||||||
const {triliumDesktopPort} = await chrome.storage.sync.get("triliumDesktopPort");
|
const {triliumDesktopPort} = await browser.storage.sync.get("triliumDesktopPort");
|
||||||
|
|
||||||
if (triliumDesktopPort) {
|
if (triliumDesktopPort) {
|
||||||
return parseInt(triliumDesktopPort);
|
return parseInt(triliumDesktopPort);
|
||||||
@ -217,10 +217,9 @@ class TriliumServerFacade {
|
|||||||
const absoff = Math.abs(off);
|
const absoff = Math.abs(off);
|
||||||
return (new Date(date.getTime() - off * 60 * 1000).toISOString().substr(0,23).replace("T", " ") +
|
return (new Date(date.getTime() - off * 60 * 1000).toISOString().substr(0,23).replace("T", " ") +
|
||||||
(off > 0 ? '-' : '+') +
|
(off > 0 ? '-' : '+') +
|
||||||
(absoff / 60).toFixed(0).padStart(2,'0') + ':' +
|
(absoff / 60).toFixed(0).padStart(2,'0') + ':' +
|
||||||
(absoff % 60).toString().padStart(2,'0'));
|
(absoff % 60).toString().padStart(2,'0'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const triliumServerFacade = new TriliumServerFacade();
|
window.triliumServerFacade = new TriliumServerFacade();
|
||||||
export { TriliumServerFacade };
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export function randomString(len) {
|
function randomString(len) {
|
||||||
let text = "";
|
let text = "";
|
||||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ export function randomString(len) {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBaseUrl() {
|
function getBaseUrl() {
|
||||||
let output = getPageLocationOrigin() + location.pathname;
|
let output = getPageLocationOrigin() + location.pathname;
|
||||||
|
|
||||||
if (output[output.length - 1] !== '/') {
|
if (output[output.length - 1] !== '/') {
|
||||||
@ -21,7 +21,7 @@ export function getBaseUrl() {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPageLocationOrigin() {
|
function getPageLocationOrigin() {
|
||||||
// location.origin normally returns the protocol + domain + port (eg. https://example.com:8080)
|
// location.origin normally returns the protocol + domain + port (eg. https://example.com:8080)
|
||||||
// but for file:// protocol this is browser dependant and in particular Firefox returns "null" in this case.
|
// but for file:// protocol this is browser dependant and in particular Firefox returns "null" in this case.
|
||||||
return location.protocol === 'file:' ? 'file://' : location.origin;
|
return location.protocol === 'file:' ? 'file://' : location.origin;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user