Last active
July 3, 2025 15:03
-
-
Save Tuurash/0c23bcfba2a463b92ed90d8fed464029 to your computer and use it in GitHub Desktop.
Turn any site into an app — impress no one but feel productive.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name PWAny | |
| // @namespace https://gist.github.com/Tuurash/0c23bcfba2a463b92ed90d8fed464029/raw/f2ffa5f8bc850dec75959dd39aa64704a356276e/PWAny.user.js | |
| // @version 1.2 | |
| // @description Inject PWA capabilities into websites via context menu | |
| // @author tuurash | |
| // @match *://*/* | |
| // @run-at document-idle | |
| // @grant GM_notification | |
| // @grant GM_registerMenuCommand | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| // Configuration | |
| const config = { | |
| checkExisting: true // Check for existing PWA elements | |
| }; | |
| let injectionStatus = { | |
| manifest: { injected: false, preExisting: false }, | |
| serviceWorker: { injected: false, preExisting: false } | |
| }; | |
| // Main initialization | |
| function init() { | |
| // Removed automatic injection | |
| registerMenuCommands(); | |
| } | |
| // Register Tampermonkey menu commands | |
| function registerMenuCommands() { | |
| if (typeof GM_registerMenuCommand !== 'undefined') { | |
| // Primary injection command | |
| GM_registerMenuCommand('Inject PWA Capabilities', () => { | |
| attemptPWAInjection(); | |
| showNotification('PWA components injected!'); | |
| }); | |
| // Status check command | |
| GM_registerMenuCommand('Show PWA Injection Status', showInjectionStatus); | |
| } | |
| } | |
| // Core injection logic | |
| function attemptPWAInjection() { | |
| checkExistingPWA(); | |
| if (!injectionStatus.manifest.preExisting) { | |
| injectManifest(); | |
| } | |
| if (!injectionStatus.serviceWorker.preExisting) { | |
| injectServiceWorker(); | |
| } | |
| showInjectionStatus(); | |
| } | |
| // Check for existing PWA components | |
| function checkExistingPWA() { | |
| // Check for existing manifest | |
| const existingManifest = document.querySelector('link[rel="manifest"]'); | |
| injectionStatus.manifest.preExisting = !!existingManifest; | |
| // Check for existing service worker | |
| if ('serviceWorker' in navigator) { | |
| navigator.serviceWorker.getRegistrations().then(registrations => { | |
| injectionStatus.serviceWorker.preExisting = registrations.length > 0; | |
| }); | |
| } | |
| } | |
| // Inject Web App Manifest | |
| function injectManifest() { | |
| try { | |
| const manifest = { | |
| name: document.title || 'PWA Enabled Site', | |
| short_name: document.title.substring(0, 12) || 'PWA Site', | |
| start_url: window.location.href, | |
| display: 'standalone', | |
| background_color: '#ffffff', | |
| theme_color: '#3f51b5', | |
| icons: [{ | |
| src: getSiteIcon(), | |
| sizes: '192x192', | |
| type: 'image/png' | |
| }] | |
| }; | |
| const blob = new Blob([JSON.stringify(manifest)], {type: 'application/json'}); | |
| const manifestURL = URL.createObjectURL(blob); | |
| const link = document.createElement('link'); | |
| link.rel = 'manifest'; | |
| link.href = manifestURL; | |
| document.head.appendChild(link); | |
| injectionStatus.manifest.injected = true; | |
| return true; | |
| } catch (e) { | |
| console.error('Manifest injection failed:', e); | |
| return false; | |
| } | |
| } | |
| // Get best available site icon | |
| function getSiteIcon() { | |
| try { | |
| const icons = [ | |
| ...document.querySelectorAll('link[rel="icon"], link[rel="shortcut icon"]'), | |
| ...document.querySelectorAll('meta[property="og:image"]') | |
| ]; | |
| return icons.length > 0 ? | |
| (icons[0].href || icons[0].content) : | |
| `${window.location.origin}/favicon.ico`; | |
| } catch (e) { | |
| return 'https://via.placeholder.com/192/3f51b5/ffffff?text=PWA'; | |
| } | |
| } | |
| // Inject Service Worker | |
| function injectServiceWorker() { | |
| if (!('serviceWorker' in navigator)) return false; | |
| try { | |
| const swCode = ` | |
| self.addEventListener('install', () => self.skipWaiting()); | |
| self.addEventListener('activate', event => event.waitUntil(self.clients.claim())); | |
| `; | |
| const blob = new Blob([swCode], {type: 'application/javascript'}); | |
| const swURL = URL.createObjectURL(blob); | |
| navigator.serviceWorker.register(swURL, { | |
| scope: './', | |
| updateViaCache: 'none' | |
| }).then(registration => { | |
| console.log('Service Worker registered:', registration); | |
| injectionStatus.serviceWorker.injected = true; | |
| }).catch(e => { | |
| console.error('Service Worker registration failed:', e); | |
| }); | |
| return true; | |
| } catch (e) { | |
| console.error('Service Worker injection failed:', e); | |
| return false; | |
| } | |
| } | |
| // Show injection status | |
| function showInjectionStatus() { | |
| const manifestStatus = injectionStatus.manifest.injected ? | |
| '✅ Injected' : injectionStatus.manifest.preExisting ? | |
| '⚠️ Pre-existing' : '❌ Not injected'; | |
| const swStatus = injectionStatus.serviceWorker.injected ? | |
| '✅ Injected' : injectionStatus.serviceWorker.preExisting ? | |
| '⚠️ Pre-existing' : '❌ Not injected'; | |
| const message = `PWA Status for ${window.location.host}: | |
| • Manifest: ${manifestStatus} | |
| • Service Worker: ${swStatus} | |
| Note: Browser may require page reload to detect changes.`; | |
| showNotification(message, 7000); | |
| } | |
| // Show notification | |
| function showNotification(message, duration = 5000) { | |
| if (typeof GM_notification === 'function') { | |
| GM_notification({ | |
| title: 'PWAny', | |
| text: message, | |
| timeout: duration, | |
| silent: true | |
| }); | |
| } else { | |
| // Fallback notification | |
| const notification = document.createElement('div'); | |
| notification.style = ` | |
| position: fixed; | |
| bottom: 20px; | |
| right: 20px; | |
| padding: 15px; | |
| background: #333; | |
| color: white; | |
| border-radius: 8px; | |
| z-index: 9999; | |
| font-family: sans-serif; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.15); | |
| max-width: 300px; | |
| white-space: pre-line; | |
| font-size: 14px; | |
| `; | |
| notification.textContent = message; | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| notification.style.opacity = '0'; | |
| notification.style.transition = 'opacity 0.5s'; | |
| setTimeout(() => notification.remove(), 500); | |
| }, duration); | |
| } | |
| } | |
| // Start the script | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', init); | |
| } else { | |
| init(); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment