diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000..f6467eb --- /dev/null +++ b/BUILD.md @@ -0,0 +1,102 @@ +# OpenImage Viewer - Build Instructions + +These instructions are provided for Mozilla Add-ons reviewers to reproduce the submitted extension package from source. + +## Build Environment + +Operating Systems Tested: + +* Windows 11 +* Ubuntu 24.04 LTS + +Required Software: + +* Node.js v22.17.1 +* pnpm 11.1.2 +* Git + +## Installation + +Clone the repository: + +```bash +git clone https://github.com/lscherub/OpenImage-Viewer.git +cd openimage-viewer +``` + +Install dependencies: + +```bash +pnpm install +``` + +## Development Build + +```bash +pnpm dev +``` + +## Production Build + +Build the extension: + +```bash +pnpm build +``` + +or for Firefox-specific builds: + +```bash +pnpm build:firefox +``` + +## Output + +The generated extension package will be created in the build output directory generated by Plasmo. + +Example locations may include: + +```text +build/ +dist/ +``` + +depending on the configured build target. + +## Package Manager + +This project uses: + +* pnpm + +Version used during release: + +```bash +pnpm --version +``` + +## Frameworks and Tooling + +The project is built with: + +* React +* TypeScript +* Plasmo Framework +* Manifest V3 + +## Source Verification + +The submitted extension package was built directly from this source tree without manual modification after the build process completed. + +## Repository + +Source repository: + +https://github.com/lscherub/OpenImage-Viewer + +## License + +MIT License + +``` +``` \ No newline at end of file diff --git a/LICENSE b/LICENSE index a6c8a5c..216be77 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2021 Adem Kouki +Copyright (c) 2026 lscherub/OpenImage Viewer Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 0000000..9405874 --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,13 @@ +# Privacy Policy + +OpenImage Viewer does not collect, store, transmit, or sell personal user data. + +The extension operates locally within the user's browser to provide image viewing and enhancement functionality. + +User settings and preferences may be stored locally using the browser's extension storage APIs for functionality purposes only. + +OpenImage Viewer does not use analytics, tracking systems, remote code execution, or advertising services. + +Some optional user-triggered features may interact with third-party websites or services, such as reverse image search providers or external image editing tools. These interactions occur only when initiated directly by the user. + +If you have questions regarding privacy, please open an issue on the GitHub repository. diff --git a/README.md b/README.md index 0388566..c6b85f0 100644 --- a/README.md +++ b/README.md @@ -1,164 +1,131 @@ -

- Logo -
- BetterViewer -

+# OpenImage Viewer -
- - - -
+**Fast advanced image viewer for Chrome & Edge** -

- Fast, Simple, Easy image viewer. -

+OpenImage Viewer is a community-maintained fork inspired by the original BetterViewer project. It preserves the existing UI/UX and toolset while adding a Manifest V3-compatible hybrid detection architecture that restores automatic image-tab opening in modern Chromium browsers. -

BetterViewer makes image viewing faster, easier, and more fun.

+## Why this fork exists -

BetterViewer was designed as a replacement for the image viewing mode built into Firefox & Chrome-based web browsers.

+When Chromium moved from MV2 to MV3, automatic behavior for **“Open image in new tab”** became unreliable in many image viewer extensions. -

With BetterViewer you can use various keyboard shortcuts to quickly pan, zoom images, edit and a lot more!

+This fork solves that with a hybrid approach: -

- - - -

+- URL-based image detection +- File extension detection +- MIME/content-type detection from response headers +- Navigation event detection +- Direct image-tab DOM detection +- Automatic redirect to the viewer page -

- BetterViewer - A chrome extension for better image viewing experience | Product Hunt -

+Fallback remains available: -

- Screenshots -

+- Right-click image → **Open Image in OpenImage Viewer** -

- Preview 1 -

+## Features -

- Preview 2 -

- -

- Preview 3 -

+- Zoom in / zoom out / 1:1 / reset +- Fullscreen +- Rotate / flip +- Crop image +- Photo editor +- Annotate image +- Color picker +- Download image +- Image details (EXIF/metadata) +- QR scanner +- Reverse image search +- Photopea integration +- Keyboard shortcuts and toolbar customization -

- Preview 3 -

+## Screenshots -### Features +![Screenshot 1](./docs/screenshot1.png) +![Screenshot 2](./docs/screenshot2.png) +![Screenshot 3](./docs/screenshot3.png) +![Screenshot 4](./docs/screenshot4.png) -ℹ️ _(Some features are not available in V2 yet due to Manifest V3 limitations, but I will add them back soon)_ +## Installation -- Zoom in / Zoom Out / Reset -- Fullscreen -- Rotate Left / Rotate Right -- Flip Horizontal / Flip Vertical -- Crop Image -- Photo Editor (Adjust, Draw, Watermark, Filters, Finetune, Resize, Export As PNG, JPEG, JPG, WEBP) -- Download Image -- Upload Image to imgBB or imgur -- Color picker -- Image Details -- Change background color (Dark / Light / Blurred) -- Print Image -- Extract Text from Image -- Edit in Photopea -- Reverse Image Search -- QR Code Scanner -- Settings to customize Toolbar +### Local development -# Changelog +```bash +pnpm install +pnpm dev +``` -## [2.0.2] - 2025-03-19 +### Build packages -- Add "Open image in new tab". -- Bug fixes. +```bash +pnpm build:chrome +pnpm build +``` -## [2.0.1] - 2025-03-11 +## MV3 compatibility notes -- New Annotation Tool – Draw and mark up images easily! -- Bug fixes and code cleanup. +This extension is built on Manifest V3 and uses a layered detection strategy to preserve auto-open behavior for direct image tabs in Chromium-based browsers. +It keeps permissions focused on what is required for: -- New version of BetterViewer is here! -- Completely rewritten from scratch (React, TypeScript, [Plasmo](https://www.plasmo.com/)) -- Using Manifest V3 (I had to remove some features because of this change, but I will add them back soon) -- Better UI/UX +- tab/navigation tracking +- content-type checks +- context menu fallback +- local state storage -## [2.0.0] - 2025-03-09 +## Keyboard shortcuts -- New version of BetterViewer is here! -- Completely rewritten from scratch (React, TypeScript, [Plasmo](https://www.plasmo.com/)) -- Using Manifest V3 (I had to remove some features because of this change, but I will add them back soon) -- Better UI/UX +Default shortcuts include: -## [1.0.5] - 2022-09-14 +- `+` / `-` / `0` for zoom controls +- `Shift + Arrow` for rotation +- `Ctrl + E` editor +- `Ctrl + X` crop +- `Shift + C` color picker +- `Ctrl + D` download +- `Ctrl + I` image details -- Add "Zoom Ratio" option to Settings. (Thanks to @Metacor) +## Supported image formats -## [1.0.4] - 2022-04-19 +Common formats include: -- Add New Photo Editor [Filerobot Image Editor](https://scaleflex.github.io/filerobot-image-editor/) (Adjust, Draw, Watermark, Filters, Finetune, Resize, Export As PNG, JPEG, JPG, WEBP) -- Fix a bug where Chrome/Edge adds a duplicate image (Thanks to @patrykdziurkowski) -- Remove Photo Editor [Painterro](https://github.com/devforth/painterro) +- JPG / JPEG / PNG / GIF / WEBP +- SVG / BMP / ICO +- AVIF +- TIFF / TIF +- APNG / JFIF / PJPEG / PJP +- HEIC / HEIF (site/browser support dependent) -## [1.0.3] - 2022-02-10 +## Known limitations -- Fix major bugs (Croppig issues, Going back issues, etc...) -- Add [Firefox](https://addons.mozilla.org/en-US/firefox/addon/betterviewer) Compatibilty -- Fix Cropping issues -- Zoom In / Zoom Out is now smoother -- Fix some UI issues -- Add new settings -- Add "Default theme" setting -- Add "Toolbar position" setting -- Add "Notification toolbar" setting -- Add "Show/Hide all tools at the start" setting +- Some websites intentionally block direct image access via headers/CSP. +- HEIC/HEIF behavior depends on browser decoding support. +- Firefox MV3 behavior may differ from Chromium behavior. -## [1.0.2] - 2021-11-28 +## Roadmap -- Fix minor bugs -- Add "Reverse Image Search" -- Add "QR Code Scanner" -- Add Settings window -- Fix Shortcuts (Combo keys / Ctrl + Key +- [ ] Refine Firefox-specific image-tab detection +- [ ] Add automated end-to-end regression tests for redirect flow +- [ ] Add refreshed store assets and icon set for OpenImage Viewer branding +- [ ] Improve onboarding and in-app settings UX -## [1.0.1] - 2021-11-20 +## Attribution -- Fix Crop problem -- Fix random shutdown -- Replace Imgur with ImgBB (Many users requested this) -- Add "Edit with Photopea" -- Add Contributors in About popup -- Edit in Photopea (Thanks to @bbbenji) +- Inspired by / based on: **[BetterViewer](https://github.com/Ademking/BetterViewer)** (original open-source project) +- License: **MIT** +- Icon attribution: **Icons made by Graphics Plazza from Flaticon** -## [1.0.0] - 2021-11-10 +## Credits -- First version +OpenImage Viewer is a community-maintained fork inspired by the original BetterViewer project by Adem Kouki. -## Contributors ✨ +Original project: +https://github.com/Ademking/BetterViewer -Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): +Includes additional Manifest V3 compatibility improvements and modern Chromium support. - - - - - - - - - -

Benji

💻

Patryk Dziurkowski

💻

Metacor

💻
+## Contributors - - +Community-maintained. Contributions are welcome via pull requests and issues. - +## License -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! +MIT License. See [LICENSE](./LICENSE). diff --git a/_options/OptionsConfig.ts b/_options/OptionsConfig.ts index aecf49f..b9585a6 100644 --- a/_options/OptionsConfig.ts +++ b/_options/OptionsConfig.ts @@ -116,8 +116,8 @@ export const config = { }, { key: "viewer-about", - name: "About BetterViewer", - description: "About BetterViewer", + name: "About OpenImage Viewer", + description: "About OpenImage Viewer", isEnable: true, }, { diff --git a/_options/index.tsx b/_options/index.tsx index 34a7c5b..7a8a9e1 100644 --- a/_options/index.tsx +++ b/_options/index.tsx @@ -89,13 +89,13 @@ const SettingsCard: React.FC = () => { return (
- BetterViewer Logo + OpenImage Viewer Logo
- BetterViewer Settings + OpenImage Viewer Settings - Customize your BetterViewer experience + Customize your OpenImage Viewer experience diff --git a/assets/icon.png b/assets/icon.png index fcc7e58..4463037 100644 Binary files a/assets/icon.png and b/assets/icon.png differ diff --git a/assets/icon.svg b/assets/icon.svg new file mode 100644 index 0000000..56d1ff0 --- /dev/null +++ b/assets/icon.svg @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="512" height="512"> +<path d="M0 0 C1.01790619 0.00215515 2.03581238 0.0043103 3.08456421 0.00653076 C17.69363523 0.05620146 32.02575853 0.30709809 46.375 3.3125 C47.34985352 3.51117676 48.32470703 3.70985352 49.32910156 3.91455078 C97.7109774 14.03517194 141.69928269 35.9516877 177.375 70.3125 C178.3640332 71.26068604 178.3640332 71.26068604 179.37304688 72.22802734 C188.86130305 81.37247479 197.6012044 90.66984009 205.375 101.3125 C206.23306152 102.46355017 207.09113451 103.61459177 207.94921875 104.765625 C213.98736829 112.95680614 219.34595521 121.47054105 224.375 130.3125 C224.73948242 130.94430176 225.10396484 131.57610352 225.47949219 132.22705078 C247.08534998 169.97532572 256.85198095 212.74189997 256.6875 255.9375 C256.68534485 256.95540619 256.6831897 257.97331238 256.68096924 259.02206421 C256.63129854 273.63113523 256.38040191 287.96325853 253.375 302.3125 C253.07698486 303.77478027 253.07698486 303.77478027 252.77294922 305.26660156 C242.65232806 353.6484774 220.7358123 397.63678269 186.375 433.3125 C185.74287598 433.97185547 185.11075195 434.63121094 184.45947266 435.31054688 C175.31502521 444.79880305 166.01765991 453.5387044 155.375 461.3125 C154.22394983 462.17056152 153.07290823 463.02863451 151.921875 463.88671875 C143.73224019 469.92427396 135.22961724 475.30707302 126.375 480.3125 C125.66778809 480.7146875 124.96057617 481.116875 124.23193359 481.53125 C101.09654164 494.5566741 75.92537591 503.30551461 49.9375 508.625 C49.21860596 508.77235596 48.49971191 508.91971191 47.7590332 509.0715332 C33.08339502 511.86090125 18.55831162 512.66142619 3.64770508 512.62817383 C0.70811513 512.6250221 -2.2304558 512.64854926 -5.16992188 512.67382812 C-18.54306192 512.71848737 -31.43916711 511.51301542 -44.625 509.3125 C-46.33055054 509.03031616 -46.33055054 509.03031616 -48.07055664 508.74243164 C-91.33802759 500.97044532 -132.38793216 480.98345309 -165.625 452.3125 C-166.37007813 451.67828125 -167.11515625 451.0440625 -167.8828125 450.390625 C-181.38779833 438.57948485 -194.05918743 425.82889772 -204.625 411.3125 C-205.48149066 410.16255169 -206.33826433 409.01281411 -207.1953125 407.86328125 C-213.23534209 399.67315742 -218.61867614 391.16870382 -223.625 382.3125 C-224.0271875 381.60528809 -224.429375 380.89807617 -224.84375 380.16943359 C-237.8691741 357.03404164 -246.61801461 331.86287591 -251.9375 305.875 C-252.08485596 305.15610596 -252.23221191 304.43721191 -252.3840332 303.6965332 C-255.35789088 288.05024391 -255.99800362 272.57681954 -255.9375 256.6875 C-255.93534485 255.66959381 -255.9331897 254.65168762 -255.93096924 253.60293579 C-255.88129854 238.99386477 -255.63040191 224.66174147 -252.625 210.3125 C-252.42632324 209.33764648 -252.22764648 208.36279297 -252.02294922 207.35839844 C-241.90232806 158.9765226 -219.9858123 114.98821731 -185.625 79.3125 C-184.99287598 78.65314453 -184.36075195 77.99378906 -183.70947266 77.31445312 C-174.56502521 67.82619695 -165.26765991 59.0862956 -154.625 51.3125 C-153.47394983 50.45443848 -152.32290823 49.59636549 -151.171875 48.73828125 C-142.98224019 42.70072604 -134.47961724 37.31792698 -125.625 32.3125 C-124.91778809 31.9103125 -124.21057617 31.508125 -123.48193359 31.09375 C-100.34654164 18.0683259 -75.17537591 9.31948539 -49.1875 4 C-48.46860596 3.85264404 -47.74971191 3.70528809 -47.0090332 3.5534668 C-31.36274391 0.57960912 -15.88931954 -0.06050362 0 0 Z " fill="#FEFEFE" transform="translate(255.625,-0.3125)"/> +<path d="M0 0 C18.57356357 10.8324843 25.35439975 32.36327397 33.375 51.125 C34.38560951 53.47272583 35.39676642 55.8202161 36.40844727 58.16748047 C44.85954976 77.80486369 53.20976759 97.48634439 61.47169495 117.20407104 C63.24059183 121.42520718 65.02027021 125.64126735 66.81884766 129.84985352 C67.18542984 130.71085663 67.55201202 131.57185974 67.92970276 132.45895386 C68.62408597 134.08890351 69.32142495 135.71759852 70.02232361 137.34475708 C75.97225554 151.34155559 76.94446426 168.17867382 71.5625 182.5625 C67.61406205 191.54011367 62.5232531 198.68954018 55.5625 205.5625 C55.02238281 206.12453125 54.48226563 206.6865625 53.92578125 207.265625 C40.08927919 220.50963201 21.87210721 222.01293328 3.82519531 221.95068359 C1.83808098 221.95475442 -0.14903109 221.96009916 -2.13613892 221.96661377 C-7.50828114 221.98023586 -12.8802496 221.97500889 -18.25239468 221.96547651 C-23.89346705 221.95784915 -29.53452515 221.96493444 -35.17559814 221.96963501 C-44.65094658 221.97508541 -54.12622475 221.96792159 -63.6015625 221.95361328 C-74.525475 221.93730705 -85.44921931 221.94256348 -96.3731277 221.9590925 C-105.77904081 221.97275032 -115.18490358 221.97460553 -124.59082341 221.96677649 C-130.19650985 221.9621211 -135.80211752 221.9614156 -141.40779877 221.97138596 C-146.68401749 221.98011537 -151.96002171 221.97392557 -157.2362175 221.95670319 C-159.16176626 221.95276923 -161.08733338 221.95381867 -163.0128746 221.96049118 C-183.56456365 222.02508713 -201.78173817 219.62293038 -217.3737793 204.89697266 C-229.31364701 192.94694682 -234.77531173 178.05221196 -234.875 161.4375 C-234.90787109 160.49390625 -234.94074219 159.5503125 -234.97460938 158.578125 C-235.09669804 142.10725483 -225.548175 127.18833751 -218.33642578 112.89379883 C-216.56390324 109.3777835 -214.80650039 105.85450902 -213.05078125 102.33007812 C-211.91234834 100.05944939 -210.77305436 97.78925212 -209.6328125 95.51953125 C-209.11590851 94.48278763 -208.59900452 93.44604401 -208.06643677 92.37788391 C-200.31384982 77.06786887 -192.4537991 64.67604608 -175.9375 58.1875 C-163.8936915 56.03681991 -153.59346705 58.91253283 -143.4375 65.5625 C-138.86273654 68.78765862 -135.48953537 72.63957204 -132 77 C-125.49577079 85.10002148 -118.63237102 92.17264987 -107.99609375 93.9765625 C-99.92981156 94.45624751 -93.56352137 91.28077945 -87.5625 86.0625 C-84.03488334 81.91236276 -80.91916938 77.90344331 -78.29296875 73.1171875 C-77.70596191 72.05322754 -77.11895508 70.98926758 -76.51416016 69.89306641 C-75.8907373 68.75272949 -75.26731445 67.61239258 -74.625 66.4375 C-73.96460408 65.23975488 -73.30362372 64.04233183 -72.64208984 62.84521484 C-69.88856333 57.85844881 -67.14776859 52.8647602 -64.41748047 47.86523438 C-41.31771611 5.57310033 -41.31771611 5.57310033 -25.328125 -1.6171875 C-16.8235772 -4.03064026 -8.0461757 -3.67883895 0 0 Z " fill="#54A6B2" transform="translate(335.4375,189.4375)"/> +<path d="M0 0 C10.08500951 7.90885609 18.52380848 18.90833512 20.42578125 31.90234375 C21.49582263 43.47948359 20.86904045 53.40258324 15.42578125 63.90234375 C14.9565625 64.81628906 14.48734375 65.73023438 14.00390625 66.671875 C7.02145467 79.19148576 -3.99661818 86.63196938 -17.57421875 90.90234375 C-32.22331687 93.86750972 -45.32818993 91.52795938 -57.94921875 83.65625 C-69.89889034 75.67202002 -76.95481428 64.0194374 -80.06640625 50.03515625 C-82.5916762 34.45618315 -78.18095867 21.61698027 -69.20703125 8.9765625 C-52.76859593 -11.96618984 -21.28651966 -14.91633155 0 0 Z " fill="#63AF7F" transform="translate(210.57421875,110.09765625)"/> +</svg> diff --git a/background.ts b/background.ts index a4e5043..7e19af4 100644 --- a/background.ts +++ b/background.ts @@ -1,47 +1,153 @@ import { Storage } from "@plasmohq/storage"; -// Create a context menu item -chrome.contextMenus.create({ - id: "captureRightClickedImg", - title: "Open Image in BetterViewer", - contexts: ["image"], +const CONTEXT_MENU_ID = "captureRightClickedImg"; +const VIEWER_PAGE_PATH = "tabs/ImageViewer.html"; +const WELCOME_PAGE_PATH = "tabs/welcome.html"; +const VIEWER_PAGE_URL = chrome.runtime.getURL(VIEWER_PAGE_PATH); + +const IMAGE_EXTENSIONS = [ + "jpg", + "jpeg", + "png", + "gif", + "webp", + "bmp", + "svg", + "avif", + "ico", + "tif", + "tiff", + "apng", + "jfif", + "pjpeg", + "pjp", + "heic", + "heif", +]; + +const pendingImageTabById = new Map<number, string>(); + +const isViewerPage = (url?: string) => !!url?.startsWith(VIEWER_PAGE_URL); + +const buildViewerUrl = (imageUrl: string) => + `${VIEWER_PAGE_URL}?url=${encodeURIComponent(imageUrl)}`; + +const looksLikeImageUrl = (url?: string) => { + if (!url) return false; + try { + const parsed = new URL(url); + if (!["http:", "https:", "file:"].includes(parsed.protocol)) { + return false; + } + const path = parsed.pathname.toLowerCase(); + return IMAGE_EXTENSIONS.some((ext) => + path.endsWith(`.${ext}`) + ); + } catch { + return false; + } +}; + +const maybeRedirectTabToViewer = async (tabId: number, imageUrl: string) => { + if (!imageUrl || isViewerPage(imageUrl)) { + return; + } + const storage = new Storage({ area: "local" }); + await storage.set("imageUrl", imageUrl); + await chrome.tabs.update(tabId, { + url: buildViewerUrl(imageUrl), + }); +}; + +const ensureContextMenu = async () => { + // Recreate to avoid duplicate-id errors when service worker restarts. + await chrome.contextMenus.removeAll(); + chrome.contextMenus.create({ + id: CONTEXT_MENU_ID, + title: "Open Image in OpenImage Viewer", + contexts: ["image"], + }); +}; + +chrome.runtime.onInstalled.addListener(async (details) => { + await ensureContextMenu(); + + // When the extension is installed, open the welcome page + if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) { + chrome.tabs.create({ + url: chrome.runtime.getURL(WELCOME_PAGE_PATH), + }); + } +}); + +chrome.runtime.onStartup.addListener(async () => { + await ensureContextMenu(); }); // When the context menu item is clicked chrome.contextMenus.onClicked.addListener(async (info, tab) => { - if (info.menuItemId === "captureRightClickedImg") { + if (info.menuItemId === CONTEXT_MENU_ID) { const imageUrl = info.srcUrl; - const storage = new Storage({ - area: "local", - }); + if (!imageUrl) return; + const storage = new Storage({ area: "local" }); await storage.set("imageUrl", imageUrl); - chrome.tabs.create({ - url: chrome.runtime.getURL("tabs/ImageViewer.html"), - }); + chrome.tabs.create({ url: buildViewerUrl(imageUrl) }); } }); -// When the extension is installed, open the welcome page -chrome.runtime.onInstalled.addListener((details) => { - if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) { - chrome.tabs.create({ - url: "https://betterviewer.surge.sh/welcome.html", - }); +chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { + if (changeInfo.status !== "complete") return; + if (!tab.url || isViewerPage(tab.url)) return; + + if (looksLikeImageUrl(tab.url)) { + await maybeRedirectTabToViewer(tabId, tab.url); + } +}); + +chrome.webRequest.onHeadersReceived.addListener( + (details) => { + if (details.tabId < 0) return; + if (details.type !== "main_frame") return; + if (isViewerPage(details.url)) return; + + const contentTypeHeader = + details.responseHeaders?.find( + (h) => h.name.toLowerCase() === "content-type" + )?.value || ""; + + if (contentTypeHeader.toLowerCase().startsWith("image/")) { + pendingImageTabById.set(details.tabId, details.url); + } + }, + { urls: ["<all_urls>"], types: ["main_frame"] }, + ["responseHeaders"] +); + +chrome.webNavigation.onCommitted.addListener(async ({ tabId, url, frameId }) => { + if (frameId !== 0) return; + if (isViewerPage(url)) return; + + const headerDetectedImageUrl = pendingImageTabById.get(tabId); + if (headerDetectedImageUrl) { + pendingImageTabById.delete(tabId); + await maybeRedirectTabToViewer(tabId, headerDetectedImageUrl); } }); // When the content script sends a message chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => { if (request.name === "openImageInNewTab") { - const tabId = sender.tab.id; + const tabId = sender.tab?.id; + if (!tabId) { + return; + } + const imageUrl = request.body.src; - const storage = new Storage({ - area: "local", - }); - await storage.set("imageUrl", imageUrl); - await chrome.tabs.update(tabId, { - url: chrome.runtime.getURL("tabs/ImageViewer.html"), - }); + if (!imageUrl) { + return; + } + + await maybeRedirectTabToViewer(tabId, imageUrl); } }); diff --git a/chrome-mv3-prod.zip b/chrome-mv3-prod.zip new file mode 100644 index 0000000..cdac215 Binary files /dev/null and b/chrome-mv3-prod.zip differ diff --git a/components/About.tsx b/components/About.tsx index 1c349ea..f250681 100644 --- a/components/About.tsx +++ b/components/About.tsx @@ -6,39 +6,41 @@ const About = () => { <div className="flex flex-col items-center justify-center h-full p-8"> <BetterViewerLogo width={150} height={150} /> <h1 className="text-4xl font-bold text-white text-center"> - BetterViewer <span className="text-blue-500 text-sm">v2.0.2</span> + OpenImage Viewer <span className="text-blue-500 text-sm">v2.0.4</span> </h1> <h3 className="text-lg text-white text-center"> - Fast, Simple & Easy Image Viewer + Fast advanced image viewer for Chrome & Edge </h3> <p className="text-white mt-4"> - BetterViewer makes image viewing faster, easier, and more fun. + OpenImage Viewer makes image viewing faster, easier, and more powerful. </p> <p className="text-white mt-2"> - Designed as a better alternative to the built-in image viewer in - Chrome-based browsers, BetterViewer lets you: + A community-maintained fork inspired by BetterViewer, designed as an + advanced replacement for built-in browser image tabs. OpenImage Viewer + lets you: <br />✅ Zoom & Pan with ease <br />✅ Edit & Enhance images instantly + <br />✅ Open image tabs automatically with MV3-compatible detection <br />✅ Use handy keyboard shortcuts for quick navigation <br />and much more! </p> <p className="text-white mt-2"> - ⭐️ If you find BetterViewer useful, don't forget to leave a star! + ⭐️ If you find OpenImage Viewer useful, please star and share the project. </p> - <a href="https://github.com/Ademking/BetterViewer" target="_blank"> + <a href="https://github.com/lscherub/BetterViewer" target="_blank"> <button className="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-1 px-4 rounded mt-2"> Star on GitHub 🌟 </button> </a> <p className="text-white mt-2"> - Created with ❤️🍪 by{" "} + Maintained with ❤️ by{" "} <a - href="https://github.com/Ademking" + href="https://github.com/lscherub" target="_blank" className="font-semibold underline" > - Adem Kouki + OpenImage Viewer Community </a> </p> </div> diff --git a/components/BetterViewerLogo.tsx b/components/BetterViewerLogo.tsx index ab09118..bae9b72 100644 --- a/components/BetterViewerLogo.tsx +++ b/components/BetterViewerLogo.tsx @@ -1,35 +1,11 @@ import * as React from "react"; const BetterViewerLogo = (props) => ( - <svg - xmlns="http://www.w3.org/2000/svg" - width={512} - height={512} - viewBox="0 0 24 24" - xmlSpace="preserve" - {...props} - > - <path - fill="#29b6f6" - d="M22.75 12a9 9 0 0 1-.07 1.15 10.743 10.743 0 0 1-21.42-.8c-.01-.11-.01-.23-.01-.35a10.75 10.75 0 0 1 21.5 0" - data-original="#29b6f6" - /> - <circle - cx={12.375} - cy={6.625} - r={2.375} - fill="#e1f5fe" - data-original="#e1f5fe" - /> - <path - fill="#1565c0" - d="M16.74 21.65a10.746 10.746 0 0 1-15.48-9.3 10.755 10.755 0 0 1 15.48 9.3" - data-original="#1565c0" - /> - <path - fill="#2196f3" - d="M22.68 13.15a10.746 10.746 0 0 1-14.42 8.93 10.285 10.285 0 0 1 10.24-9.83 10.1 10.1 0 0 1 4.18.9" - data-original="#2196f3" - /> - </svg> + <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="512" height="512" + + viewBox="0 0 512 512" fill="none" {...props}> +<path d="M0 0 C1.01790619 0.00215515 2.03581238 0.0043103 3.08456421 0.00653076 C17.69363523 0.05620146 32.02575853 0.30709809 46.375 3.3125 C47.34985352 3.51117676 48.32470703 3.70985352 49.32910156 3.91455078 C97.7109774 14.03517194 141.69928269 35.9516877 177.375 70.3125 C178.3640332 71.26068604 178.3640332 71.26068604 179.37304688 72.22802734 C188.86130305 81.37247479 197.6012044 90.66984009 205.375 101.3125 C206.23306152 102.46355017 207.09113451 103.61459177 207.94921875 104.765625 C213.98736829 112.95680614 219.34595521 121.47054105 224.375 130.3125 C224.73948242 130.94430176 225.10396484 131.57610352 225.47949219 132.22705078 C247.08534998 169.97532572 256.85198095 212.74189997 256.6875 255.9375 C256.68534485 256.95540619 256.6831897 257.97331238 256.68096924 259.02206421 C256.63129854 273.63113523 256.38040191 287.96325853 253.375 302.3125 C253.07698486 303.77478027 253.07698486 303.77478027 252.77294922 305.26660156 C242.65232806 353.6484774 220.7358123 397.63678269 186.375 433.3125 C185.74287598 433.97185547 185.11075195 434.63121094 184.45947266 435.31054688 C175.31502521 444.79880305 166.01765991 453.5387044 155.375 461.3125 C154.22394983 462.17056152 153.07290823 463.02863451 151.921875 463.88671875 C143.73224019 469.92427396 135.22961724 475.30707302 126.375 480.3125 C125.66778809 480.7146875 124.96057617 481.116875 124.23193359 481.53125 C101.09654164 494.5566741 75.92537591 503.30551461 49.9375 508.625 C49.21860596 508.77235596 48.49971191 508.91971191 47.7590332 509.0715332 C33.08339502 511.86090125 18.55831162 512.66142619 3.64770508 512.62817383 C0.70811513 512.6250221 -2.2304558 512.64854926 -5.16992188 512.67382812 C-18.54306192 512.71848737 -31.43916711 511.51301542 -44.625 509.3125 C-46.33055054 509.03031616 -46.33055054 509.03031616 -48.07055664 508.74243164 C-91.33802759 500.97044532 -132.38793216 480.98345309 -165.625 452.3125 C-166.37007813 451.67828125 -167.11515625 451.0440625 -167.8828125 450.390625 C-181.38779833 438.57948485 -194.05918743 425.82889772 -204.625 411.3125 C-205.48149066 410.16255169 -206.33826433 409.01281411 -207.1953125 407.86328125 C-213.23534209 399.67315742 -218.61867614 391.16870382 -223.625 382.3125 C-224.0271875 381.60528809 -224.429375 380.89807617 -224.84375 380.16943359 C-237.8691741 357.03404164 -246.61801461 331.86287591 -251.9375 305.875 C-252.08485596 305.15610596 -252.23221191 304.43721191 -252.3840332 303.6965332 C-255.35789088 288.05024391 -255.99800362 272.57681954 -255.9375 256.6875 C-255.93534485 255.66959381 -255.9331897 254.65168762 -255.93096924 253.60293579 C-255.88129854 238.99386477 -255.63040191 224.66174147 -252.625 210.3125 C-252.42632324 209.33764648 -252.22764648 208.36279297 -252.02294922 207.35839844 C-241.90232806 158.9765226 -219.9858123 114.98821731 -185.625 79.3125 C-184.99287598 78.65314453 -184.36075195 77.99378906 -183.70947266 77.31445312 C-174.56502521 67.82619695 -165.26765991 59.0862956 -154.625 51.3125 C-153.47394983 50.45443848 -152.32290823 49.59636549 -151.171875 48.73828125 C-142.98224019 42.70072604 -134.47961724 37.31792698 -125.625 32.3125 C-124.91778809 31.9103125 -124.21057617 31.508125 -123.48193359 31.09375 C-100.34654164 18.0683259 -75.17537591 9.31948539 -49.1875 4 C-48.46860596 3.85264404 -47.74971191 3.70528809 -47.0090332 3.5534668 C-31.36274391 0.57960912 -15.88931954 -0.06050362 0 0 Z " fill="#FEFEFE" transform="translate(255.625,-0.3125)"/> +<path d="M0 0 C18.57356357 10.8324843 25.35439975 32.36327397 33.375 51.125 C34.38560951 53.47272583 35.39676642 55.8202161 36.40844727 58.16748047 C44.85954976 77.80486369 53.20976759 97.48634439 61.47169495 117.20407104 C63.24059183 121.42520718 65.02027021 125.64126735 66.81884766 129.84985352 C67.18542984 130.71085663 67.55201202 131.57185974 67.92970276 132.45895386 C68.62408597 134.08890351 69.32142495 135.71759852 70.02232361 137.34475708 C75.97225554 151.34155559 76.94446426 168.17867382 71.5625 182.5625 C67.61406205 191.54011367 62.5232531 198.68954018 55.5625 205.5625 C55.02238281 206.12453125 54.48226563 206.6865625 53.92578125 207.265625 C40.08927919 220.50963201 21.87210721 222.01293328 3.82519531 221.95068359 C1.83808098 221.95475442 -0.14903109 221.96009916 -2.13613892 221.96661377 C-7.50828114 221.98023586 -12.8802496 221.97500889 -18.25239468 221.96547651 C-23.89346705 221.95784915 -29.53452515 221.96493444 -35.17559814 221.96963501 C-44.65094658 221.97508541 -54.12622475 221.96792159 -63.6015625 221.95361328 C-74.525475 221.93730705 -85.44921931 221.94256348 -96.3731277 221.9590925 C-105.77904081 221.97275032 -115.18490358 221.97460553 -124.59082341 221.96677649 C-130.19650985 221.9621211 -135.80211752 221.9614156 -141.40779877 221.97138596 C-146.68401749 221.98011537 -151.96002171 221.97392557 -157.2362175 221.95670319 C-159.16176626 221.95276923 -161.08733338 221.95381867 -163.0128746 221.96049118 C-183.56456365 222.02508713 -201.78173817 219.62293038 -217.3737793 204.89697266 C-229.31364701 192.94694682 -234.77531173 178.05221196 -234.875 161.4375 C-234.90787109 160.49390625 -234.94074219 159.5503125 -234.97460938 158.578125 C-235.09669804 142.10725483 -225.548175 127.18833751 -218.33642578 112.89379883 C-216.56390324 109.3777835 -214.80650039 105.85450902 -213.05078125 102.33007812 C-211.91234834 100.05944939 -210.77305436 97.78925212 -209.6328125 95.51953125 C-209.11590851 94.48278763 -208.59900452 93.44604401 -208.06643677 92.37788391 C-200.31384982 77.06786887 -192.4537991 64.67604608 -175.9375 58.1875 C-163.8936915 56.03681991 -153.59346705 58.91253283 -143.4375 65.5625 C-138.86273654 68.78765862 -135.48953537 72.63957204 -132 77 C-125.49577079 85.10002148 -118.63237102 92.17264987 -107.99609375 93.9765625 C-99.92981156 94.45624751 -93.56352137 91.28077945 -87.5625 86.0625 C-84.03488334 81.91236276 -80.91916938 77.90344331 -78.29296875 73.1171875 C-77.70596191 72.05322754 -77.11895508 70.98926758 -76.51416016 69.89306641 C-75.8907373 68.75272949 -75.26731445 67.61239258 -74.625 66.4375 C-73.96460408 65.23975488 -73.30362372 64.04233183 -72.64208984 62.84521484 C-69.88856333 57.85844881 -67.14776859 52.8647602 -64.41748047 47.86523438 C-41.31771611 5.57310033 -41.31771611 5.57310033 -25.328125 -1.6171875 C-16.8235772 -4.03064026 -8.0461757 -3.67883895 0 0 Z " fill="#54A6B2" transform="translate(335.4375,189.4375)"/> +<path d="M0 0 C10.08500951 7.90885609 18.52380848 18.90833512 20.42578125 31.90234375 C21.49582263 43.47948359 20.86904045 53.40258324 15.42578125 63.90234375 C14.9565625 64.81628906 14.48734375 65.73023438 14.00390625 66.671875 C7.02145467 79.19148576 -3.99661818 86.63196938 -17.57421875 90.90234375 C-32.22331687 93.86750972 -45.32818993 91.52795938 -57.94921875 83.65625 C-69.89889034 75.67202002 -76.95481428 64.0194374 -80.06640625 50.03515625 C-82.5916762 34.45618315 -78.18095867 21.61698027 -69.20703125 8.9765625 C-52.76859593 -11.96618984 -21.28651966 -14.91633155 0 0 Z " fill="#63AF7F" transform="translate(210.57421875,110.09765625)"/> +</svg> ); export default BetterViewerLogo; diff --git a/components/Help.tsx b/components/Help.tsx index 0dd81db..0ea0867 100644 --- a/components/Help.tsx +++ b/components/Help.tsx @@ -39,23 +39,23 @@ const Help = () => { const faqs = [ { - question: "Which browsers support BetterViewer?", + question: "Which browsers support OpenImage Viewer?", answer: - "BetterViewer works on all major modern browsers, including Chrome, Firefox, Safari, and Edge.", + "OpenImage Viewer is optimized for Chromium browsers, including Chrome and Microsoft Edge.", }, { - question: "Is BetterViewer available on mobile devices?", + question: "Is OpenImage Viewer available on mobile devices?", answer: - "Not yet. BetterViewer is currently designed for desktop browsers. However, some mobile browsers may support desktop extensions.", + "Not yet. OpenImage Viewer is currently designed for desktop browsers.", }, { - question: "How can I contribute to BetterViewer?", + question: "How can I contribute to OpenImage Viewer?", answer: ( <span> - You can support BetterViewer by reporting bugs, suggesting new + You can support OpenImage Viewer by reporting bugs, suggesting new features, or contributing code on GitHub. <a - href="https://github.com/Ademking/Betterviewer" + href="https://github.com/lscherub/BetterViewer" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline ml-1" @@ -68,12 +68,12 @@ const Help = () => { { question: "Why are some features from the old version missing?", answer: - "BetterViewer has been fully rebuilt to support Manifest V3, which resulted in some temporary feature removals. We’re working hard to restore them. If there's a feature you’d like back, let us know!", + "OpenImage Viewer includes MV3-compatible hybrid detection to restore automatic image-tab opening in modern Chromium browsers.", }, { - question: "Is BetterViewer free to use?", + question: "Is OpenImage Viewer free to use?", answer: - "Yes, BetterViewer is completely free to use with no hidden costs or subscription fees and It's open-source.", + "Yes. OpenImage Viewer is free and open-source under the MIT license.", }, ]; @@ -104,7 +104,7 @@ const Help = () => { <h1 className="text-3xl font-bold mt-6 mb-4 border-b border-gray-700 pb-2"> Features </h1> - <p className="mb-4 text-lg">What can you do with BetterViewer?</p> + <p className="mb-4 text-lg">What can you do with OpenImage Viewer?</p> <ul className="space-y-2 pl-5 list-disc text-gray-300"> {features.map((item, index) => ( diff --git a/config/ImageViewerConfig.ts b/config/ImageViewerConfig.ts index ca73743..71cfbf2 100644 --- a/config/ImageViewerConfig.ts +++ b/config/ImageViewerConfig.ts @@ -273,7 +273,7 @@ export const tipppyOptions = [ }, { selector: ".viewer-about", - text: "About BetterViewer", + text: "About OpenImage Viewer", }, { selector: ".viewer-qr", diff --git a/content.ts b/content.ts index cf0f871..b2e09ff 100644 --- a/content.ts +++ b/content.ts @@ -8,20 +8,43 @@ export const config: PlasmoCSConfig = { }; try { + let hasForwarded = false; + + const forwardImageToViewer = () => { + if (hasForwarded) { + return; + } + + const img = document.body?.querySelector("img"); + const src = img?.currentSrc || img?.getAttribute("src"); + if (!src) { + return; + } + + hasForwarded = true; + sendToBackground({ + name: "openImageInNewTab" as never, + body: { + src, + }, + }); + }; + const isNewImageTabChromium = () => { - // is body styled correctly - const isStyledCorrectly = - document.body.getAttribute("style") === - "margin: 0px; height: 100%; background-color: rgb(14, 14, 14);"; - // is body has only one child and it is image - const hasSingleImageChild = - document.body.children.length === 1 && - document.body.children[0]?.tagName === "IMG"; - // is image src matching - const isImageSrcMatching = - document.body.children[0]?.getAttribute("src") === window.location.href; - - return isStyledCorrectly && hasSingleImageChild && isImageSrcMatching; + if (!document.body) { + return false; + } + + const images = document.body.querySelectorAll("img"); + if (images.length !== 1) { + return false; + } + + // Chromium image documents usually contain only the image node and no app UI. + const hasSingleElementChild = document.body.children.length === 1; + const bodyHasNoText = (document.body.textContent || "").trim().length === 0; + + return hasSingleElementChild && bodyHasNoText; }; const isNewImageTabFirefox = () => { @@ -48,15 +71,40 @@ try { // ); }; - if (isNewImageTabChromium() || isNewImageTabFirefox()) { - console.log("This is a new image tab"); - sendToBackground({ - name: "openImageInNewTab" as never, - body: { - src: document.body.children[0]?.getAttribute("src"), - }, + const tryInterceptImageTab = () => { + if (isNewImageTabChromium() || isNewImageTabFirefox()) { + forwardImageToViewer(); + } + }; + + // Try immediately when script runs. + tryInterceptImageTab(); + + // Image documents can finalize structure slightly after document_end. + const observer = new MutationObserver(() => { + tryInterceptImageTab(); + if (hasForwarded) { + observer.disconnect(); + } + }); + + if (document.documentElement) { + observer.observe(document.documentElement, { + childList: true, + subtree: true, }); } + + window.addEventListener( + "load", + () => { + tryInterceptImageTab(); + if (hasForwarded) { + observer.disconnect(); + } + }, + { once: true } + ); } catch (error) { console.error("An error occurred:", error); } diff --git a/docs/1.png b/docs/1.png new file mode 100644 index 0000000..28fb0fd Binary files /dev/null and b/docs/1.png differ diff --git a/docs/2.png b/docs/2.png new file mode 100644 index 0000000..f4574a7 Binary files /dev/null and b/docs/2.png differ diff --git a/docs/3.png b/docs/3.png new file mode 100644 index 0000000..549b2d1 Binary files /dev/null and b/docs/3.png differ diff --git a/docs/4.png b/docs/4.png new file mode 100644 index 0000000..e85d1ee Binary files /dev/null and b/docs/4.png differ diff --git a/docs/STORE_LISTING.md b/docs/STORE_LISTING.md new file mode 100644 index 0000000..738bd0a --- /dev/null +++ b/docs/STORE_LISTING.md @@ -0,0 +1,86 @@ +# OpenImage Viewer — Store Listing Kit + +## Product Name +OpenImage Viewer + +## Subtitle +Fast advanced image viewer for Chrome & Edge + +## Short Description (Chrome/Edge) +Open images in a powerful viewer tab with zoom, rotate, crop, editor tools, and MV3-compatible auto-detection. + +## Long Description +OpenImage Viewer is a fast, advanced browser image viewer for Chrome and Microsoft Edge. It is a community-maintained fork inspired by BetterViewer, with a Manifest V3-compatible hybrid detection architecture that restores automatic image-viewing behavior for modern Chromium browsers. + +When you open an image in a new tab, OpenImage Viewer can automatically redirect it into a full-featured viewer experience using layered detection: + +- URL and extension checks +- MIME/content-type checks +- navigation event detection +- direct image-tab detection + +If automatic interception is unavailable on a site, fallback support remains available via context menu: + +Right-click image → Open Image in OpenImage Viewer + +Core tools include zoom controls, fullscreen mode, rotate/flip, crop, annotation, photo editing, color picker, QR scanning, EXIF details, reverse image search, Photopea integration, and keyboard shortcuts for productivity. + +OpenImage Viewer is lightweight, privacy-conscious, and built to preserve performance while improving image workflows for researchers, designers, developers, and everyday users. + +## SEO Keywords +- image viewer +- open image +- image tab viewer +- fullscreen image viewer +- advanced image viewer +- browser image viewer +- image zoom +- image tools +- photo viewer +- open image in new tab + +## Suggested Categories/Tags +- Chrome Web Store category: **Productivity** +- Edge Add-ons category: **Productivity** +- Tags: image viewer, photo tools, productivity, browser utility, image zoom + +## Store Asset Checklist + +### Screenshots +- [ ] Image opened in new tab auto-redirecting into viewer +- [ ] Zoom/rotate/flip toolbar +- [ ] Crop + editor workflow +- [ ] Color picker + image details panel +- [ ] Keyboard shortcuts/help panel + +### Promotional Art +- [ ] 128x128 icon (store icon) +- [ ] 440x280 small promo tile (Chrome) +- [ ] 920x680 marquee tile (Chrome, optional but recommended) +- [ ] Edge spotlight banner variants + +## Privacy Policy Template + +OpenImage Viewer processes image URLs and image-page context locally in your browser to provide image viewing features. + +What we collect: +- We do not collect personal data for sale. +- We do not run third-party trackers inside the extension runtime. + +What we store: +- Local extension settings (toolbar and viewer preferences). +- Temporary image URL state needed to open viewer tabs. + +Network behavior: +- The extension may open external tools only when user-invoked (for example, reverse image search or Photopea). + +Contact: +- Support URL: https://github.com/lscherub/BetterViewer/issues + +## Attribution Snippets + +### Store listing attribution +OpenImage Viewer is a community-maintained fork inspired by the original BetterViewer project, released under the MIT License. + +### Icon attribution +Icons made by Graphics Plazza from Flaticon. diff --git a/docs/Untitled design (3).zip b/docs/Untitled design (3).zip new file mode 100644 index 0000000..03a700f Binary files /dev/null and b/docs/Untitled design (3).zip differ diff --git a/docs/welcome.html b/docs/welcome.html index 8769632..1a01d71 100644 --- a/docs/welcome.html +++ b/docs/welcome.html @@ -5,16 +5,7 @@ <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>Welcome to BetterViewer! - - - + Welcome to OpenImage Viewer! @@ -28,9 +19,11 @@ hero
-

Thank you for installing BetterViewer! 🎉

-

BetterViewer makes image viewing faster, easier, and more fun.

-

BetterViewer was designed as a replacement for the image viewing mode +

Thank you for installing OpenImage Viewer! 🎉

+

Fast advanced image viewing for Chrome and Edge.

+

OpenImage Viewer is a community-maintained fork inspired by BetterViewer, + with MV3-compatible automatic image-tab detection and redirect support.

+

It was designed as a replacement for the image viewing mode built into web browsers.

@@ -41,7 +34,7 @@

Thank you for installing Bet Right-click and select context-menu - Open Image in BetterViewer + Open Image in OpenImage Viewer

Thank you for installing Bet alt="hero" src="dog.jpg">

-
-