[READ-ONLY] Mirror of https://github.com/probablykasper/thumbnail-grabber. Chrome extension for grabbing thumbnails/covers
browser-extension chrome chrome-extension chrome-extention extension
0

Configure Feed

Select the types of activity you want to include in your feed.

Add ci

Kasper (Sep 26, 2022, 7:35 AM +0200) 3c3906c9 caa618ad

+89 -72
+11
.github/workflows/ci.yml
··· 1 + name: CI 2 + on: [push, pull_request, workflow_dispatch] 3 + jobs: 4 + ci: 5 + runs-on: ubuntu-latest 6 + steps: 7 + - name: Checkout repository 8 + uses: actions/checkout@v2 9 + 10 + - run: npm install 11 + - run: npm run check
+24 -17
README.md
··· 17 17 18 18 You can click the extension icon, use keyboard shortcuts or right click the page or links. You can customize the keyboard shortcuts, what clicking the extension icon does, and what context menu items are visible. 19 19 20 - # Dev Instructions 20 + ## Dev Instructions 21 21 22 - ## Folder info 23 - `/src` 24 - `/src/modules` 25 - Files that should not be compiled/copies should be in the `/src/modules` folder. 22 + ### Setup 23 + 1. Install Node.js 24 + 2. Run `npm install` to install dependencies 26 25 27 - ## Setup 28 - 1. Install [Node.js](https://nodejs.org/) (Version 12.11 or later recommended) 29 - 2. Run `npm install` to install dependencies 26 + ### Commands 27 + - `npm run dev`: Build extension and watch for changes 28 + - `npm run build`: Build extension and create a zip in `/dist`, ready for distribution to stores 29 + - `npm run format`: Format code and apply code suggestions 30 + - `npm run check`: Check code 30 31 31 - ## Commands 32 - Build `/src` into `/build` and watch for changes. You can then load the `/build` folder as an unpacked extension in your browser. 33 - ``` 34 - npm run dev 35 - ``` 32 + ## Permissions 33 + - `storage`: For storing the extension's settings 34 + - `contextMenus`: For the ability to open/copy/download from the right-click menu 35 + - `activeTab`: For inserting the popup into the current website 36 + - `clipboardWrite`: For copying thumbnails to the clipboard 37 + - `<all_urls>`: For checking if a thumbnail URL exists 38 + - Individual URLs: For showing the download popup on certain websites 36 39 37 - Zip the extension into `/dist`, ready to be uploaded to the Chrome Web Store and such. You'll be prompted to type in the version number. 38 - ``` 39 - npm run zip 40 - ``` 40 + ### Publish new version 41 + 1. Run `npm run check` 42 + 2. Update `CHANGELOG.md` 43 + 3. Update version in `manifest.json` 44 + 4. Run `npm run build` 45 + 5. Commit with a tag in format "v#.#.#" 46 + 6. Create GitHub release with the extension zip and release notes 47 + 7. Publish to Chrome and Firefox stores
+11 -13
src/background.ts
··· 14 14 action(tab.id, { type: 'open' }); 15 15 }); 16 16 17 - chrome.runtime.onMessage.addListener( 18 - function (msg, _sender, _sendResponse) { 19 - if (msg.type === 'options') { 20 - chrome.runtime.openOptionsPage(); 21 - } else if (msg.type === 'copyblob') { 22 - try { 23 - browser.clipboard.setImageData(msg.arrayBuffer, msg.imageType); 24 - return Promise.resolve({ success: true }); 25 - } catch (_error) { 26 - return Promise.resolve({ success: false }); 27 - } 17 + chrome.runtime.onMessage.addListener(function (msg, _sender, _sendResponse) { 18 + if (msg.type === 'options') { 19 + chrome.runtime.openOptionsPage(); 20 + } else if (msg.type === 'copyblob') { 21 + try { 22 + browser.clipboard.setImageData(msg.arrayBuffer, msg.imageType); 23 + return Promise.resolve({ success: true }); 24 + } catch (_error) { 25 + return Promise.resolve({ success: false }); 28 26 } 29 - }, 30 - ); 27 + } 28 + }); 31 29 32 30 chrome.commands.onCommand.addListener(function (command) { 33 31 if (['open', 'download', 'copy'].includes(command)) {
+43 -42
src/content-script.ts
··· 21 21 var notificationElement = document.createElement('div'); 22 22 notificationElement.innerHTML = notificationInnerHTML; 23 23 notificationElement.classList.add('thumbnail-grabber-notification'); 24 - const lastChild = notificationElement.querySelector('p:last-child') 24 + const lastChild = notificationElement.querySelector('p:last-child'); 25 25 if (!(lastChild instanceof HTMLElement)) { 26 26 throw alert('No p:last-child'); 27 27 } 28 28 lastChild.textContent = msg; 29 29 30 - const firstChild = notificationElement.querySelector('p:first-child') 30 + const firstChild = notificationElement.querySelector('p:first-child'); 31 31 if (!(firstChild instanceof HTMLElement)) { 32 32 throw alert('No p:first-child'); 33 33 } 34 - firstChild.addEventListener( 35 - 'click', 36 - function () { 37 - notificationElement.style.animation = 'none'; 38 - requestAnimationFrame(function () { 39 - notificationElement.classList.add( 40 - 'thumbnail-grabber-notification-removing', 41 - ); 42 - setTimeout(function () { 43 - if (notificationElement.parentElement) { 44 - notificationElement.parentElement.removeChild(notificationElement); 45 - } 46 - }, 500); 47 - }); 48 - }, 49 - ); 34 + firstChild.addEventListener('click', function () { 35 + notificationElement.style.animation = 'none'; 36 + requestAnimationFrame(function () { 37 + notificationElement.classList.add( 38 + 'thumbnail-grabber-notification-removing', 39 + ); 40 + setTimeout(function () { 41 + if (notificationElement.parentElement) { 42 + notificationElement.parentElement.removeChild(notificationElement); 43 + } 44 + }, 500); 45 + }); 46 + }); 50 47 51 48 var icon = chrome.runtime.getURL('icon48.png'); 52 - const iconImgElement = notificationElement.querySelector('img') 49 + const iconImgElement = notificationElement.querySelector('img'); 53 50 if (!(iconImgElement instanceof HTMLElement)) { 54 51 throw alert('No iconImgElement'); 55 52 } ··· 271 268 async function copyEventListener(e) { 272 269 e.preventDefault(); 273 270 try { 274 - const result = await copy(lastImageUrl) 271 + const result = await copy(lastImageUrl); 275 272 if (result && result.as) { 276 273 notify(`Copied as ${result.as}`); 277 274 } ··· 416 413 const canvas = document.createElement('canvas'); 417 414 const ctx = canvas.getContext('2d'); 418 415 if (!ctx) { 419 - throw alert('No 2d canvas ctx') 416 + throw alert('No 2d canvas ctx'); 420 417 } 421 418 const imageEl = createImage({ src: imageUrl }); 422 419 imageEl.onload = (e) => { ··· 458 455 } 459 456 460 457 async function firefoxCopy(url) { 461 - const response = await fetch(url); 462 - const arrayBuffer = await response.arrayBuffer(); 463 - const contentType = response.headers.get('Content-Type'); 464 - let imageType 465 - if (contentType === 'image/jpeg' || contentType === 'image/jpg') { 466 - imageType = 'jpeg' 467 - } else if (contentType === 'image/png') { 468 - imageType = 'png' 469 - } else if (url.endsWith('.jpeg') || url.endsWith('.jpg')) { 470 - imageType = 'jpeg' 471 - } else if (url.endsWith('.png')) { 472 - imageType = 'png' 473 - } else { 474 - throw new Error('Unknown image type') 475 - } 476 - const error = await chrome.runtime.sendMessage({ type: 'copyblob', arrayBuffer, imageType, }); 477 - if (!error?.success) { 478 - throw new Error('setImageData not supported') 479 - } 458 + const response = await fetch(url); 459 + const arrayBuffer = await response.arrayBuffer(); 460 + const contentType = response.headers.get('Content-Type'); 461 + let imageType; 462 + if (contentType === 'image/jpeg' || contentType === 'image/jpg') { 463 + imageType = 'jpeg'; 464 + } else if (contentType === 'image/png') { 465 + imageType = 'png'; 466 + } else if (url.endsWith('.jpeg') || url.endsWith('.jpg')) { 467 + imageType = 'jpeg'; 468 + } else if (url.endsWith('.png')) { 469 + imageType = 'png'; 470 + } else { 471 + throw new Error('Unknown image type'); 472 + } 473 + const error = await chrome.runtime.sendMessage({ 474 + type: 'copyblob', 475 + arrayBuffer, 476 + imageType, 477 + }); 478 + if (!error?.success) { 479 + throw new Error('setImageData not supported'); 480 + } 480 481 } 481 482 482 483 async function copy(url) { 483 484 try { 484 - await firefoxCopy(url) 485 + await firefoxCopy(url); 485 486 } catch (_error) { 486 487 const imageResponse = await fetch(url); 487 488 const imgBlob = await imageResponse.blob(); ··· 489 490 await copyToClipboard(imgBlob); 490 491 } else { 491 492 await convertToPng(imgBlob); 492 - return { as: 'PNG' } 493 + return { as: 'PNG' }; 493 494 } 494 495 } 495 496 }