Universal Zoom Control

Add zoom in/out buttons to every website

  1. // ==UserScript==
  2. // @name Universal Zoom Control
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Add zoom in/out buttons to every website
  6. // @author You
  7. // @match *://*/*
  8. // @grant none
  9. // @run-at document-start
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14. let currentZoom = 1.0;
  15. let zoomContainer = null;
  16. let isHidden = false;
  17. let transparency = 0.9; // Default transparency (0.1 = very transparent, 1.0 = opaque)
  18. // Create zoom controls
  19. function createZoomControls() {
  20. // Create container
  21. zoomContainer = document.createElement('div');
  22. zoomContainer.id = 'zoom-controls';
  23. updateContainerStyle();
  24. // Create zoom out button
  25. const zoomOutBtn = document.createElement('button');
  26. zoomOutBtn.innerHTML = '−';
  27. zoomOutBtn.title = 'Zoom Out (Ctrl + -)';
  28. zoomOutBtn.style.cssText = `
  29. background: #4CAF50;
  30. color: white;
  31. border: none;
  32. width: 32px;
  33. height: 32px;
  34. border-radius: 6px;
  35. cursor: pointer;
  36. font-size: 18px;
  37. font-weight: bold;
  38. margin-right: 4px;
  39. transition: background 0.2s;
  40. `;
  41. zoomOutBtn.onmouseover = () => zoomOutBtn.style.background = '#45a049';
  42. zoomOutBtn.onmouseout = () => zoomOutBtn.style.background = '#4CAF50';
  43. zoomOutBtn.onclick = () => zoomOut();
  44. // Create zoom level display
  45. const zoomLevel = document.createElement('span');
  46. zoomLevel.id = 'zoom-level';
  47. zoomLevel.style.cssText = `
  48. color: white;
  49. font-size: 12px;
  50. font-weight: bold;
  51. margin: 0 8px;
  52. min-width: 35px;
  53. text-align: center;
  54. display: inline-block;
  55. `;
  56. zoomLevel.textContent = '100%';
  57. // Create zoom in button
  58. const zoomInBtn = document.createElement('button');
  59. zoomInBtn.innerHTML = '+';
  60. zoomInBtn.title = 'Zoom In (Ctrl + +)';
  61. zoomInBtn.style.cssText = `
  62. background: #2196F3;
  63. color: white;
  64. border: none;
  65. width: 32px;
  66. height: 32px;
  67. border-radius: 6px;
  68. cursor: pointer;
  69. font-size: 18px;
  70. font-weight: bold;
  71. margin-left: 4px;
  72. transition: background 0.2s;
  73. `;
  74. zoomInBtn.onmouseover = () => zoomInBtn.style.background = '#1976D2';
  75. zoomInBtn.onmouseout = () => zoomInBtn.style.background = '#2196F3';
  76. zoomInBtn.onclick = () => zoomIn();
  77. // Create transparency slider
  78. const transparencyContainer = document.createElement('div');
  79. transparencyContainer.style.cssText = `
  80. margin-top: 8px;
  81. padding-top: 8px;
  82. border-top: 1px solid rgba(255, 255, 255, 0.2);
  83. display: flex;
  84. align-items: center;
  85. gap: 6px;
  86. `;
  87. const transparencyLabel = document.createElement('span');
  88. transparencyLabel.textContent = '👁';
  89. transparencyLabel.style.cssText = `
  90. color: white;
  91. font-size: 12px;
  92. `;
  93. const transparencySlider = document.createElement('input');
  94. transparencySlider.type = 'range';
  95. transparencySlider.min = '0.3';
  96. transparencySlider.max = '1.0';
  97. transparencySlider.step = '0.1';
  98. transparencySlider.value = transparency;
  99. transparencySlider.style.cssText = `
  100. width: 60px;
  101. height: 4px;
  102. background: rgba(255, 255, 255, 0.3);
  103. outline: none;
  104. border-radius: 2px;
  105. `;
  106. transparencySlider.oninput = (e) => {
  107. transparency = parseFloat(e.target.value);
  108. updateContainerStyle();
  109. saveSettings();
  110. };
  111. transparencyContainer.appendChild(transparencyLabel);
  112. transparencyContainer.appendChild(transparencySlider);
  113. // Create hide/show toggle button
  114. const toggleBtn = document.createElement('button');
  115. toggleBtn.innerHTML = '👁';
  116. toggleBtn.title = 'Hide/Show Controls (Alt + H)';
  117. toggleBtn.style.cssText = `
  118. background: #9C27B0;
  119. color: white;
  120. border: none;
  121. width: 32px;
  122. height: 32px;
  123. border-radius: 6px;
  124. cursor: pointer;
  125. font-size: 12px;
  126. margin-left: 8px;
  127. transition: background 0.2s;
  128. `;
  129. toggleBtn.onmouseover = () => toggleBtn.style.background = '#7B1FA2';
  130. toggleBtn.onmouseout = () => toggleBtn.style.background = '#9C27B0';
  131. toggleBtn.onclick = () => toggleVisibility();
  132. // Create reset button
  133. const resetBtn = document.createElement('button');
  134. resetBtn.innerHTML = '⌂';
  135. resetBtn.title = 'Reset Zoom (Ctrl + 0)';
  136. resetBtn.style.cssText = `
  137. background: #FF9800;
  138. color: white;
  139. border: none;
  140. width: 32px;
  141. height: 32px;
  142. border-radius: 6px;
  143. cursor: pointer;
  144. font-size: 14px;
  145. margin-left: 8px;
  146. transition: background 0.2s;
  147. `;
  148. resetBtn.onmouseover = () => resetBtn.style.background = '#F57C00';
  149. resetBtn.onmouseout = () => resetBtn.style.background = '#FF9800';
  150. resetBtn.onclick = () => resetZoom();
  151. // Create main controls container
  152. const mainControls = document.createElement('div');
  153. mainControls.id = 'main-controls';
  154. mainControls.style.cssText = `
  155. display: flex;
  156. align-items: center;
  157. `;
  158. // Assemble main controls
  159. mainControls.appendChild(zoomOutBtn);
  160. mainControls.appendChild(zoomLevel);
  161. mainControls.appendChild(zoomInBtn);
  162. mainControls.appendChild(resetBtn);
  163. mainControls.appendChild(toggleBtn);
  164. // Assemble all controls
  165. zoomContainer.appendChild(mainControls);
  166. zoomContainer.appendChild(transparencyContainer);
  167. // Add to page
  168. document.body.appendChild(zoomContainer);
  169. // Load saved settings
  170. loadSettings();
  171. // Make draggable
  172. makeDraggable(zoomContainer);
  173. }
  174. // Update container transparency and style
  175. function updateContainerStyle() {
  176. if (!zoomContainer) return;
  177. const bgAlpha = transparency * 0.8; // Background is slightly more transparent
  178. zoomContainer.style.cssText = `
  179. position: fixed;
  180. top: 20px;
  181. right: 20px;
  182. z-index: 999999;
  183. background: rgba(0, 0, 0, ${bgAlpha});
  184. border-radius: 8px;
  185. padding: 8px;
  186. box-shadow: 0 4px 12px rgba(0, 0, 0, ${transparency * 0.3});
  187. font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  188. user-select: none;
  189. backdrop-filter: blur(10px);
  190. opacity: ${transparency};
  191. transition: opacity 0.3s ease, transform 0.3s ease;
  192. cursor: grab;
  193. `;
  194. // Apply hidden state if needed
  195. if (isHidden) {
  196. zoomContainer.style.transform = 'translateX(calc(100% - 40px))';
  197. zoomContainer.style.opacity = '0.3';
  198. } else {
  199. zoomContainer.style.transform = 'translateX(0)';
  200. }
  201. }
  202. // Toggle visibility
  203. function toggleVisibility() {
  204. isHidden = !isHidden;
  205. updateContainerStyle();
  206. saveSettings();
  207. // Change toggle button appearance
  208. const toggleBtn = zoomContainer.querySelector('button[title*="Hide/Show"]');
  209. if (toggleBtn) {
  210. toggleBtn.innerHTML = isHidden ? '👀' : '👁';
  211. toggleBtn.title = isHidden ? 'Show Controls (Alt + H)' : 'Hide/Show Controls (Alt + H)';
  212. }
  213. }
  214. // Zoom functions
  215. function zoomIn() {
  216. currentZoom = Math.min(currentZoom + 0.1, 3.0);
  217. applyZoom();
  218. }
  219. function zoomOut() {
  220. currentZoom = Math.max(currentZoom - 0.1, 0.5);
  221. applyZoom();
  222. }
  223. function resetZoom() {
  224. currentZoom = 1.0;
  225. applyZoom();
  226. }
  227. function applyZoom() {
  228. document.body.style.zoom = currentZoom;
  229. document.body.style.transform = `scale(${currentZoom})`;
  230. document.body.style.transformOrigin = 'top left';
  231. // Update zoom level display
  232. const zoomLevelElement = document.getElementById('zoom-level');
  233. if (zoomLevelElement) {
  234. zoomLevelElement.textContent = Math.round(currentZoom * 100) + '%';
  235. }
  236. // Save zoom level and settings to localStorage
  237. saveSettings();
  238. }
  239. // Save settings to localStorage
  240. function saveSettings() {
  241. try {
  242. const settings = {
  243. zoom: currentZoom,
  244. transparency: transparency,
  245. hidden: isHidden,
  246. position: {
  247. left: zoomContainer ? zoomContainer.style.left : '',
  248. top: zoomContainer ? zoomContainer.style.top : '',
  249. right: zoomContainer ? zoomContainer.style.right : ''
  250. }
  251. };
  252. localStorage.setItem('userscript-zoom-settings', JSON.stringify(settings));
  253. } catch (e) {
  254. // Ignore localStorage errors
  255. }
  256. }
  257. // Load saved settings
  258. function loadSettings() {
  259. try {
  260. const savedSettings = localStorage.getItem('userscript-zoom-settings');
  261. if (savedSettings) {
  262. const settings = JSON.parse(savedSettings);
  263. currentZoom = settings.zoom || 1.0;
  264. transparency = settings.transparency || 0.9;
  265. isHidden = settings.hidden || false;
  266. // Apply zoom
  267. applyZoom();
  268. // Update transparency slider
  269. const slider = zoomContainer.querySelector('input[type="range"]');
  270. if (slider) slider.value = transparency;
  271. // Apply position
  272. if (settings.position && settings.position.left) {
  273. zoomContainer.style.left = settings.position.left;
  274. zoomContainer.style.top = settings.position.top;
  275. zoomContainer.style.right = 'auto';
  276. }
  277. // Update container style with saved settings
  278. updateContainerStyle();
  279. // Update toggle button if hidden
  280. if (isHidden) {
  281. const toggleBtn = zoomContainer.querySelector('button[title*="Hide/Show"]');
  282. if (toggleBtn) {
  283. toggleBtn.innerHTML = '👀';
  284. toggleBtn.title = 'Show Controls (Alt + H)';
  285. }
  286. }
  287. }
  288. } catch (e) {
  289. // Ignore localStorage errors
  290. }
  291. }
  292. // Make element draggable
  293. function makeDraggable(element) {
  294. let isDragging = false;
  295. let dragOffset = { x: 0, y: 0 };
  296. element.onmousedown = function(e) {
  297. // Don't drag if clicking on interactive elements
  298. if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON') {
  299. return;
  300. }
  301. isDragging = true;
  302. dragOffset.x = e.clientX - element.offsetLeft;
  303. dragOffset.y = e.clientY - element.offsetTop;
  304. element.style.cursor = 'grabbing';
  305. e.preventDefault();
  306. };
  307. document.onmousemove = function(e) {
  308. if (!isDragging) return;
  309. const newX = e.clientX - dragOffset.x;
  310. const newY = e.clientY - dragOffset.y;
  311. // Keep within viewport bounds
  312. const maxX = window.innerWidth - element.offsetWidth;
  313. const maxY = window.innerHeight - element.offsetHeight;
  314. element.style.left = Math.max(0, Math.min(newX, maxX)) + 'px';
  315. element.style.top = Math.max(0, Math.min(newY, maxY)) + 'px';
  316. element.style.right = 'auto';
  317. // Save position
  318. saveSettings();
  319. };
  320. document.onmouseup = function() {
  321. isDragging = false;
  322. element.style.cursor = 'grab';
  323. };
  324. element.style.cursor = 'grab';
  325. }
  326. // Keyboard shortcuts
  327. function setupKeyboardShortcuts() {
  328. document.addEventListener('keydown', function(e) {
  329. if (e.ctrlKey || e.metaKey) {
  330. switch(e.key) {
  331. case '+':
  332. case '=':
  333. e.preventDefault();
  334. zoomIn();
  335. break;
  336. case '-':
  337. e.preventDefault();
  338. zoomOut();
  339. break;
  340. case '0':
  341. e.preventDefault();
  342. resetZoom();
  343. break;
  344. }
  345. }
  346. // Alt + H for hide/show toggle
  347. if (e.altKey && e.key.toLowerCase() === 'h') {
  348. e.preventDefault();
  349. toggleVisibility();
  350. }
  351. });
  352. }
  353. // Initialize when DOM is ready
  354. function init() {
  355. if (document.body) {
  356. createZoomControls();
  357. setupKeyboardShortcuts();
  358. } else {
  359. // Wait for body to be available
  360. const observer = new MutationObserver(function(mutations) {
  361. if (document.body) {
  362. observer.disconnect();
  363. createZoomControls();
  364. setupKeyboardShortcuts();
  365. }
  366. });
  367. observer.observe(document.documentElement, { childList: true, subtree: true });
  368. }
  369. }
  370. // Start initialization
  371. if (document.readyState === 'loading') {
  372. document.addEventListener('DOMContentLoaded', init);
  373. } else {
  374. init();
  375. }
  376. })();