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)
}
}
})