diff --git a/package-lock.json b/package-lock.json index bbbad8b..16067e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "nimv3", - "version": "3.11.0", + "version": "3.11.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "nimv3", - "version": "3.11.0", + "version": "3.11.1", "dependencies": { "@auth0/auth0-vue": "^2.3.3", "@vitejs/plugin-vue": "^5.1.4", @@ -15,6 +15,7 @@ "async": "^3.2.6", "jwt-decode": "^4.0.0", "nanoid": "^5.0.8", + "posthog-js": "^1.187.1", "socket.io-client": "^4.8.1", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1", @@ -585,9 +586,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", - "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1734,6 +1735,17 @@ "dev": true, "license": "MIT" }, + "node_modules/core-js": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -1754,9 +1766,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -2344,6 +2356,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -3412,9 +3430,9 @@ "license": "MIT" }, "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "license": "MIT", "dependencies": { @@ -3829,6 +3847,28 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/posthog-js": { + "version": "1.187.1", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.187.1.tgz", + "integrity": "sha512-d2oNtdqXWqTVGmJf131xmVepcI/waYn9L+dM7CKm/58p89ua6ABY699UJpKlAZ9YT0UBE09QsVjrDHYCK2F5Rw==", + "license": "MIT", + "dependencies": { + "core-js": "^3.38.1", + "fflate": "^0.4.8", + "preact": "^10.19.3", + "web-vitals": "^4.2.0" + } + }, + "node_modules/preact": { + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4877,6 +4917,12 @@ } } }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "license": "Apache-2.0" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 57d7e26..50e6c6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nimv3", - "version": "3.11.1", + "version": "3.12.0", "scripts": { "dev": "vite --host", "dev:extension": "npm-run-all --parallel build:dev watch:rollup:deps", @@ -25,6 +25,7 @@ "async": "^3.2.6", "jwt-decode": "^4.0.0", "nanoid": "^5.0.8", + "posthog-js": "^1.187.1", "socket.io-client": "^4.8.1", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1", diff --git a/src/scripting.js b/src/scripting.js index 42128c7..b88dffd 100644 --- a/src/scripting.js +++ b/src/scripting.js @@ -13,4 +13,32 @@ } imageEl.style.transform = `rotate(${menuItemId.split('-')[1]}deg)` } + scripting.saveAsWebp = (options) => { + const { srcUrl, quality } = options + const img = document.querySelector(`img[src="${srcUrl}"]`) + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + + // Set canvas size to match image + canvas.width = img.naturalWidth + canvas.height = img.naturalHeight + + // Draw the image onto the canvas + ctx.drawImage(img, 0, 0) + + // Convert to WebP data URL + const webpDataUrl = canvas.toDataURL('image/webp', quality/100) + + let filename = new URL(srcUrl).pathname.split('/').pop() + const extension = filename.split('.').pop() + filename = filename ? filename.replace(`.${extension}`, '.webp') : 'image.webp' + + // Create a download link and trigger download + const link = document.createElement('a') + link.href = webpDataUrl + link.download = filename + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + } })(typeof module !== 'undefined' && module.exports ? module.exports : (self.scripting = self.scripting || {})) \ No newline at end of file diff --git a/src/ui/App.vue b/src/ui/App.vue index 0d57359..2e3b7a2 100644 --- a/src/ui/App.vue +++ b/src/ui/App.vue @@ -12,9 +12,10 @@ + notifications diff --git a/src/utilities.js b/src/utilities.js index 64e9e0f..e2e1f29 100644 --- a/src/utilities.js +++ b/src/utilities.js @@ -40,6 +40,10 @@ utilities.rotate270 = (info, tab) => utilities.rotate(270, info, tab) chrome.contextMenus.removeAll(() => { + chrome.contextMenus.create({ title: 'Image SaveAs (webp q=1)', id: 'saveas-webp-100', contexts: ['image'] }, () => { }) + chrome.contextMenus.create({ title: 'Image SaveAs (webp q=0.9)', id: 'saveas-webp-90', contexts: ['image'] }, () => { }) + chrome.contextMenus.create({ title: 'Image SaveAs (webp q=0.5)', id: 'saveas-webp-50', contexts: ['image'] }, () => { }) + chrome.contextMenus.create({ title: 'Image Rotate (0deg)', id: 'rotate-0', contexts: ['image'] }, () => { }) chrome.contextMenus.create({ title: 'Image Rotate (90deg)', id: 'rotate-90', contexts: ['image'] }, () => { }) chrome.contextMenus.create({ title: 'Image Rotate (180deg)', id: 'rotate-180', contexts: ['image'] }, () => { }) @@ -62,6 +66,24 @@ chrome.windows.update(tab.windowId, { width, height }, () => { googleAnalytics.fireEvent('Window Resize', { 'size': `${width}x${height}` }) }) + } else if (/saveas-/.test(menuItemId)) { + const quality = Number(menuItemId.replace(/saveas-[^-]*-/, '')) + const type = menuItemId.match(/saveas-([^-]*)-.*/)[1] + + chrome.scripting.executeScript( + { + target: { tabId: tab.id, allFrames: true }, + func: scripting.saveAsWebp, + args: [{ type, quality, ...info }] + }, + (injectionResults) => { + if (!injectionResults) return + for (const frameResult of injectionResults) { + if (settings.debugVerbosity >= 7) { + console.log('Utilities:saveAs:InjectionResult: ' + frameResult) + } + } + }) } else { chrome.scripting.executeScript( { @@ -73,7 +95,7 @@ if (!injectionResults) return for (const frameResult of injectionResults) { if (settings.debugVerbosity >= 7) { - console.log('Utilities:InjectionResult: ' + frameResult) + console.log('Utilities:rotate:InjectionResult: ' + frameResult) } } })