// ==UserScript== // @name Reopen recent URLs (cross-tab history) // @namespace https://github.com/chirag127/userscripts // @version 0.1.0 // @description Keep a rolling ring-buffer of the last 30 URLs you closed across all tabs. Menu command opens an overlay listing them; click any to reopen in a new tab. Survives tab-close and browser restart via GM_setValue. // @author chirag127 // @match *://*/* // @run-at document-idle // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_openInTab // @license MIT // @homepageURL https://github.com/chirag127/userscripts/tree/main/scripts/reopen-recent-urls // @supportURL https://github.com/chirag127/userscripts/issues // @updateURL https://raw.githubusercontent.com/chirag127/userscripts/main/scripts/reopen-recent-urls/reopen-recent-urls.user.js // @downloadURL https://raw.githubusercontent.com/chirag127/userscripts/main/scripts/reopen-recent-urls/reopen-recent-urls.user.js // ==/UserScript== (() => { 'use strict' const KEY = 'recent-urls' const MAX = 30 function load() { const v = GM_getValue(KEY, []) return Array.isArray(v) ? v : [] } function save(list) { GM_setValue(KEY, list.slice(0, MAX)) } // Push current page onto the ring buffer, dedupe by URL (most-recent wins). function record() { const url = location.href if (!url || url.startsWith('about:') || url.startsWith('chrome:')) return const list = load().filter(e => e.url !== url) list.unshift({ url, title: document.title || url, timestamp: Date.now() }) save(list) } // beforeunload fires on tab close, navigation, refresh — captures the URL the // user is leaving. pagehide covers Safari/bfcache cases where beforeunload skips. window.addEventListener('beforeunload', record) window.addEventListener('pagehide', record) // ---- overlay ------------------------------------------------------------ let overlay = null function closeOverlay() { if (overlay) { overlay.remove(); overlay = null } } function fmtAge(ts) { const s = Math.floor((Date.now() - ts) / 1000) if (s < 60) return `${s}s ago` if (s < 3600) return `${Math.floor(s / 60)}m ago` if (s < 86400) return `${Math.floor(s / 3600)}h ago` return `${Math.floor(s / 86400)}d ago` } function showOverlay() { closeOverlay() const list = load() overlay = document.createElement('div') Object.assign(overlay.style, { position: 'fixed', inset: '0', zIndex: '2147483647', background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif', }) overlay.addEventListener('click', e => { if (e.target === overlay) closeOverlay() }) const panel = document.createElement('div') Object.assign(panel.style, { background: '#1e1e1e', color: '#eee', borderRadius: '8px', padding: '16px', maxWidth: '720px', width: '90vw', maxHeight: '80vh', overflow: 'auto', boxShadow: '0 10px 40px rgba(0,0,0,0.5)', }) panel.innerHTML = `