mirror of
https://github.com/zadam/trilium.git
synced 2026-02-11 08:14:39 +01:00
feat(web-clipper): handle manifest V3
This commit is contained in:
parent
a9b8ffd94c
commit
e37487a1cf
@ -1,7 +1,6 @@
|
||||
import { randomString } from "../../utils";
|
||||
import TriliumServerFacade, { isDevEnv } from "./trilium_server_facade";
|
||||
import { randomString, Rect } from "@/utils";
|
||||
|
||||
type Rect = { x: number, y: number, width: number, height: number };
|
||||
import TriliumServerFacade, { isDevEnv } from "./trilium_server_facade";
|
||||
|
||||
export default defineBackground(() => {
|
||||
const triliumServerFacade = new TriliumServerFacade();
|
||||
@ -23,38 +22,46 @@ export default defineBackground(() => {
|
||||
}
|
||||
});
|
||||
|
||||
function cropImage(newArea: Rect, dataUrl: string) {
|
||||
return new Promise<string>((resolve) => {
|
||||
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: Rect) {
|
||||
async function takeCroppedScreenshot(cropRect: Rect, devicePixelRatio: number = 1) {
|
||||
const activeTab = await getActiveTab();
|
||||
const zoom = await browser.tabs.getZoom(activeTab.id) * window.devicePixelRatio;
|
||||
const zoom = await browser.tabs.getZoom(activeTab.id) * devicePixelRatio;
|
||||
|
||||
const newArea: Rect = Object.assign({}, cropRect);
|
||||
newArea.x *= zoom;
|
||||
newArea.y *= zoom;
|
||||
newArea.width *= zoom;
|
||||
newArea.height *= zoom;
|
||||
const newArea: Rect = {
|
||||
x: cropRect.x * zoom,
|
||||
y: cropRect.y * zoom,
|
||||
width: cropRect.width * zoom,
|
||||
height: cropRect.height * zoom
|
||||
};
|
||||
|
||||
const dataUrl = await browser.tabs.captureVisibleTab({ format: 'png' });
|
||||
|
||||
return await cropImage(newArea, dataUrl);
|
||||
// Create offscreen document if it doesn't exist
|
||||
await ensureOffscreenDocument();
|
||||
|
||||
// Send cropping task to offscreen document
|
||||
const croppedDataUrl = await browser.runtime.sendMessage({
|
||||
type: 'CROP_IMAGE',
|
||||
dataUrl,
|
||||
cropRect: newArea
|
||||
});
|
||||
|
||||
return croppedDataUrl;
|
||||
}
|
||||
|
||||
async function ensureOffscreenDocument() {
|
||||
const existingContexts = await browser.runtime.getContexts({
|
||||
contextTypes: ['OFFSCREEN_DOCUMENT']
|
||||
});
|
||||
|
||||
if (existingContexts.length > 0) {
|
||||
return; // Already exists
|
||||
}
|
||||
|
||||
await browser.offscreen.createDocument({
|
||||
url: browser.runtime.getURL('/offscreen.html'),
|
||||
reasons: ['DOM_SCRAPING'], // or 'DISPLAY_MEDIA' depending on browser support
|
||||
justification: 'Image cropping requires canvas API'
|
||||
});
|
||||
}
|
||||
|
||||
async function takeWholeScreenshot() {
|
||||
@ -224,9 +231,9 @@ export default defineBackground(() => {
|
||||
}
|
||||
|
||||
async function saveCroppedScreenshot(pageUrl) {
|
||||
const cropRect = await sendMessageToActiveTab({name: 'trilium-get-rectangle-for-screenshot'});
|
||||
const { rect, devicePixelRatio } = await sendMessageToActiveTab({name: 'trilium-get-rectangle-for-screenshot'});
|
||||
|
||||
const src = await takeCroppedScreenshot(cropRect);
|
||||
const src = await takeCroppedScreenshot(rect, devicePixelRatio);
|
||||
|
||||
const payload = await getImagePayloadFromSrc(src, pageUrl);
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { Rect } from "@/utils.js";
|
||||
|
||||
import Readability from "../../lib/Readability.js";
|
||||
import { createLink, getBaseUrl, getPageLocationOrigin, randomString } from "../../utils.js";
|
||||
|
||||
@ -69,7 +71,7 @@ export default defineContentScript({
|
||||
}
|
||||
|
||||
function getRectangleArea() {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise<Rect>((resolve) => {
|
||||
const overlay = document.createElement('div');
|
||||
overlay.style.opacity = '0.6';
|
||||
overlay.style.background = 'black';
|
||||
@ -120,7 +122,7 @@ export default defineContentScript({
|
||||
|
||||
let isDragging = false;
|
||||
let draggingStartPos: {x: number, y: number} | null = null;
|
||||
let selectionArea: {x?: number, y?: number, width?: number, height?: number} = {};
|
||||
let selectionArea: Rect;
|
||||
|
||||
function updateSelection() {
|
||||
selection.style.left = `${selectionArea.x}px`;
|
||||
@ -300,7 +302,10 @@ export default defineContentScript({
|
||||
|
||||
}
|
||||
else if (message.name === 'trilium-get-rectangle-for-screenshot') {
|
||||
return getRectangleArea();
|
||||
return {
|
||||
rect: await getRectangleArea(),
|
||||
devicePixelRatio: window.devicePixelRatio
|
||||
};
|
||||
}
|
||||
else if (message.name === "trilium-save-page") {
|
||||
const {title, body} = getReadableDocument();
|
||||
|
||||
9
apps/web-clipper/entrypoints/offscreen/index.html
Normal file
9
apps/web-clipper/entrypoints/offscreen/index.html
Normal file
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="./index.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
24
apps/web-clipper/entrypoints/offscreen/index.ts
Normal file
24
apps/web-clipper/entrypoints/offscreen/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||
if (message.type === 'CROP_IMAGE') {
|
||||
cropImage(message.cropRect, message.dataUrl).then(sendResponse);
|
||||
return true; // Keep channel open for async response
|
||||
}
|
||||
});
|
||||
|
||||
function cropImage(newArea: { x: number, y: number, width: number, height: number }, dataUrl: string) {
|
||||
return new Promise<string>((resolve) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = newArea.width;
|
||||
canvas.height = newArea.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.drawImage(img, newArea.x, newArea.y, newArea.width, newArea.height,
|
||||
0, 0, newArea.width, newArea.height);
|
||||
}
|
||||
resolve(canvas.toDataURL());
|
||||
};
|
||||
img.src = dataUrl;
|
||||
});
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
export type Rect = { x: number, y: number, width: number, height: number };
|
||||
|
||||
export function randomString(len: number) {
|
||||
let text = "";
|
||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
@ -12,7 +12,8 @@ export default defineConfig({
|
||||
"https://*/",
|
||||
"<all_urls>",
|
||||
"storage",
|
||||
"contextMenus"
|
||||
"contextMenus",
|
||||
"offscreen"
|
||||
],
|
||||
browser_specific_settings: {
|
||||
gecko: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user