{"id":49879000,"url":"https://github.com/paytonison/hover-zoom","last_synced_at":"2026-05-15T13:13:59.514Z","repository":{"id":336606545,"uuid":"1128184868","full_name":"paytonison/hover-zoom","owner":"paytonison","description":"Safari/Tampermonkey userscript for near-cursor image previews and Alt/Option-click popout overlays for images, background images, and videos.","archived":false,"fork":false,"pushed_at":"2026-05-05T23:39:11.000Z","size":119,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-06T01:28:14.800Z","etag":null,"topics":["image-popout","image-preview","javascript","safari","safari-userscript","tampermonkey","userscript","video-popout"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paytonison.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-05T09:07:38.000Z","updated_at":"2026-05-05T23:39:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/paytonison/hover-zoom","commit_stats":null,"previous_names":["paytonison/hover-zoom"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/paytonison/hover-zoom","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paytonison%2Fhover-zoom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paytonison%2Fhover-zoom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paytonison%2Fhover-zoom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paytonison%2Fhover-zoom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paytonison","download_url":"https://codeload.github.com/paytonison/hover-zoom/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paytonison%2Fhover-zoom/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33067702,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-15T11:35:32.926Z","status":"ssl_error","status_checked_at":"2026-05-15T11:35:31.362Z","response_time":103,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["image-popout","image-preview","javascript","safari","safari-userscript","tampermonkey","userscript","video-popout"],"created_at":"2026-05-15T13:13:58.304Z","updated_at":"2026-05-15T13:13:59.506Z","avatar_url":"https://github.com/paytonison.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hover-zoom\n\n`hover-zoom` contains a single Safari/Tampermonkey userscript, `Image Popout\n(Safari)`, for inspecting media on web pages without leaving the page. It shows\nnear-cursor previews for supported images and videos, and it can open supported\nmedia in a draggable, resizable in-page popout.\n\n## Repository contents\n\n- `src/Image Popout (Safari)-2.0.1.user.js`: the userscript to install.\n- `README.md`: this usage and development guide.\n\nThere is no build step, package manifest, lint command, or automated test suite.\n\n## What the userscript does\n\n- Runs on top-level `http` and `https` pages.\n- Shows a hover preview for supported images and videos when the target is at\n  least 48 by 48 CSS pixels.\n- Keeps hover previews inside the viewport and scales them down when needed.\n- Lets `Z` turn hover previews on or off, saved per site in `localStorage` under\n  `image_popout_safari_v2`.\n- Lets `P` pin or unpin the current hover preview.\n- Opens a modal popout with Alt/Option-click on supported image or video media.\n- Auto-fits the popout to the media, then allows manual dragging and resizing.\n- Provides popout controls for `Copy URL`, `Open`, `Download`, and `Close`.\n- Lets `D` download the media currently open in the popout.\n- Closes the hover preview and popout with `Esc`; clicking the popout backdrop\n  also closes the popout.\n\n## Supported media\n\nThe script uses generic media discovery plus small site-aware URL handling where\ncommon sites need it. It searches the element under the pointer and nearby\nelements at the same screen coordinates for:\n\n- `\u003cimg\u003e` elements, preferring the largest `srcset` candidate when one exists.\n- `currentSrc`, `src`, and common lazy-load attributes: `data-src`,\n  `data-original`, `data-url`, `data-lazy-src`, `data-zoom-src`, `data-hires`,\n  `data-full`, and `data-large`.\n- Direct image links around an image or under the pointer.\n- CSS `background-image` URLs on common container elements.\n- `\u003cvideo\u003e` elements, including `currentSrc`, `src`, and nested `\u003csource\u003e` URLs.\n- Direct video links for popouts and hover previews.\n- Wikipedia/Wikimedia thumbnails, media-viewer file hashes, and direct upload\n  URLs, including thumbnail URLs rewritten back to the original upload path when\n  possible.\n- Instagram images and videos when the page exposes them as ordinary media\n  elements, background images, or direct media links.\n- `twimg.com` image URLs, with `name=orig` requested when the URL uses Twitter's\n  `format` or `name` query parameters.\n\nFor hover video previews, direct video URLs are replayed in the preview. Blob or\nMediaSource-backed videos are moved into the preview temporarily when possible,\nwith poster/frame fallbacks used when direct replay is not available.\n\n## Installation\n\n1. Install a Safari-compatible userscript manager.\n2. Prefer a manager with `GM_download` and `GM_xmlhttpRequest` support if you\n   want the built-in download control to work reliably.\n3. Create a new userscript from\n   `src/Image Popout (Safari)-2.0.1.user.js`.\n4. Enable it and refresh any pages you want to test.\n\nThe userscript metadata currently matches every `http` and `https` page:\n\n```js\n// @match        http://*/*\n// @match        https://*/*\n```\n\nIt also declares:\n\n```js\n// @grant        GM_download\n// @grant        GM_xmlhttpRequest\n// @connect      *\n```\n\nThose grants are used only for the download flow.\n\n## Controls\n\n| Action                            | Result                                            |\n| --------------------------------- | ------------------------------------------------- |\n| Hover a supported media target    | Show a near-cursor preview                        |\n| `P` while a preview is visible    | Pin or unpin the current preview                  |\n| `Z`                               | Toggle hover previews on or off                   |\n| `Esc`                             | Hide the preview and close the popout             |\n| Alt/Option-click supported media  | Open the media in the popout                      |\n| Drag the popout title bar         | Move the popout                                   |\n| Drag the bottom-right handle      | Resize the popout                                 |\n| `Copy URL`                        | Copy the popout media URL, with a prompt fallback |\n| `Open`                            | Open the popout media URL in a new tab            |\n| `Download` or `D`                 | Download the current popout media                 |\n| `Close`, backdrop click, or `Esc` | Close the popout                                  |\n\n## Download behavior\n\nDownloads first try the userscript manager's `GM_download` API. If that is not\navailable or fails, the script tries `GM_xmlhttpRequest` to fetch the media as a\nblob and then saves it through a temporary download link.\n\nGenerated filenames use this pattern:\n\n```text\nhz_\u003csite-host\u003e_\u003cyyyy-mm-dd\u003e_\u003chh-mm-ss\u003e.\u003cextension\u003e\n```\n\nThe extension is inferred from the response `Content-Type`, the URL path, or\ncommon query parameters when possible. Otherwise, `.bin` is used.\n\n## Known limits\n\n- The script exits inside iframes and only runs in the top window.\n- Download reliability depends on the userscript manager, direct media access,\n  and the site's response headers.\n- Some dynamic video players expose only blob or MediaSource URLs. Hover preview\n  may still work by temporarily moving the live video element, but popout and\n  download need a direct media URL.\n\n## Development and testing\n\nEdit `src/Image Popout (Safari)-2.0.1.user.js` directly, reload the userscript in\nSafari, and manually verify behavior on real pages. Useful checks include hover\npreviews, keyboard pinning, Alt/Option-click popouts, dragging, resizing,\ncopy/open/download controls, keyboard shortcuts, and normal page click/scroll\nbehavior.\n\nFor a quick syntax check, run:\n\n```sh\nnode --check 'src/Image Popout (Safari)-2.0.1.user.js'\n```\n\n## Security and privacy\n\n- The script does not send browsing data to a separate service.\n- It does not fetch or execute remote code.\n- It reads media URLs from the current page and may request those URLs only when\n  loading previews, opening popouts, or downloading media.\n- Treat page content as untrusted input when extending the script.\n\n## Development Philosophy\n\nI made this userscript because the Safari userscripts I found were slow, bloated, or overloaded with unnecessary features. The core problem was simple: display a full-resolution image or video preview from a thumbnail without requiring the user to click through and load the media directly.\n\nThis userscript is built specifically for Safari. It uses the browser’s native behavior to improve responsiveness while keeping the feature set focused and lightweight. It works with images and videos on most sites that expose media assets in the page, including sites that wrap media in containers, such as Wikipedia and Instagram. Because it is Safari-native, I also added a little Liquid Glass flair.\n\nThe development process was agent-assisted, but not passive. I collaborated with ChatGPT to reason through the design, especially because web development and media-serving edge cases are not my usual domain. We worked through the initial concept, refined the feature behavior, assembled implementation prompts, and then used Codex to generate and modify the code.\n\nAs with any software project, issues appeared during testing. I would have Codex write or update the code, test the script directly in Safari, evaluate whether the behavior was actually useful, and identify bugs or edge cases. Then I would report the problem back in concrete terms and direct the next fix. Through that loop, bugs were squashed, edge cases were handled, and the userscript became progressively more robust.\n\nWhen I wanted a new feature, the process repeated: discuss the idea with ChatGPT, understand the relevant web-development or media-handling mechanics, turn that into a precise prompt, give it to Codex, test the result, and iterate.\n\nThis is the development pattern I think agentic programming is moving toward. Right now, coding agents are often treated as “insert words, get program,” which is dismissed as “vibe coding”: the developer accepts whatever the agent produces and keeps moving until something breaks. My process is different.\n\nI use ChatGPT and Codex as collaborators in the development loop: discussing features, formulating implementation plans, directing changes, testing software live, identifying failures, and iterating when bugs or useful surprises emerge. The agent is not just autocomplete, and the human is not just a prompt dispenser. The result is a workflow where the human provides architecture, judgment, testing, taste, and direction, while the agent accelerates implementation.\n\nI believe this kind of collaboration will become a major part of software development as agents become more deeply integrated into programming workflows. The relevant skill will not only be raw coding ability, but also the ability to use agents effectively: specifying intent, supervising implementation, testing behavior, recognizing edge cases, and turning a rough generated artifact into working software.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaytonison%2Fhover-zoom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaytonison%2Fhover-zoom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaytonison%2Fhover-zoom/lists"}