您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Floating theme settings menu for zed.city with invert colors, hue overlay, custom background, page title change, hide logo, and reset options.
// ==UserScript== // @name Zed.City Theme Settings // @namespace http://tampermonkey.net/ // @version 1.1.8 // @description Floating theme settings menu for zed.city with invert colors, hue overlay, custom background, page title change, hide logo, and reset options. // @author ChatGPT // @match https://www.zed.city/* // @grant none // ==/UserScript== (function() { 'use strict'; // --- Constants --- const LS_KEYS = { themeColor: 'zedThemeColor', invertColors: 'zedInvertColors', hideLogo: 'zedHideLogo', pageTitle: 'zedPageTitle', customBackground: 'zedCustomBackground', hueOverlayColor: 'zedHueOverlayColor', hueOverlayIntensity: 'zedHueOverlayIntensity', menuMinimized: 'zedMenuMinimized', }; const DEFAULTS = { themeColor: '#000000', invertColors: false, hideLogo: false, pageTitle: document.title || 'zed.city', customBackground: '', hueOverlayColor: '#ff69b4', hueOverlayIntensity: 0, menuMinimized: false, }; // --- Utility functions for localStorage --- function lsGet(key, def) { try { const val = localStorage.getItem(key); if (val === null || val === undefined) return def; if (typeof def === 'boolean') return val === 'true'; if (typeof def === 'number') return Number(val) || def; return val; } catch { return def; } } function lsSet(key, val) { try { localStorage.setItem(key, val); } catch {} } // --- Create elements helper --- function el(tag, options = {}) { const e = document.createElement(tag); if (options.text) e.textContent = options.text; if (options.html) e.innerHTML = options.html; if (options.attrs) { for (const [k,v] of Object.entries(options.attrs)) e.setAttribute(k,v); } if (options.styles) { for (const [k,v] of Object.entries(options.styles)) e.style[k] = v; } if (options.classes) { for (const c of options.classes) e.classList.add(c); } if (options.events) { for (const [k,v] of Object.entries(options.events)) e.addEventListener(k,v); } return e; } // --- Global variables --- let menu, minimizedBtn; let colorInput, invertCheckbox, hideLogoCheckbox, pageTitleInput, bgInput, hueColorInput, hueIntensityInput; let resetBtn, minimizeBtn; // --- Apply functions --- function applyThemeColor(color) { document.documentElement.style.setProperty('--zed-theme-color', color); // Change clickable buttons and minimize/minimized backgrounds const buttons = document.querySelectorAll('button, a, input[type=button], input[type=submit]'); buttons.forEach(btn => { btn.style.backgroundColor = color; btn.style.color = '#fff'; btn.style.borderColor = color; }); if (minimizeBtn) minimizeBtn.style.backgroundColor = color; if (minimizedBtn) minimizedBtn.style.backgroundColor = color; if (hueIntensityInput) hueIntensityInput.style.accentColor = color; } function applyInvertColors(enabled) { if (enabled) { if (!document.getElementById('zedInvertStyle')) { const style = document.createElement('style'); style.id = 'zedInvertStyle'; style.textContent = ` html { filter: invert(1) hue-rotate(180deg); background: black !important; } img, video, svg, picture, iframe, .no-invert { filter: invert(1) hue-rotate(180deg) !important; } `; document.head.appendChild(style); } } else { const style = document.getElementById('zedInvertStyle'); if (style) style.remove(); } } function applyHideLogo(hide) { // Try to find logo by typical selectors const logo = document.querySelector('header img.logo, .logo img, header .logo, .logo'); if (logo) { logo.style.display = hide ? 'none' : ''; } } function applyPageTitle(title) { if (!title) title = DEFAULTS.pageTitle; document.title = title; } function applyCustomBackground(dataUrl) { if (dataUrl) { document.body.style.backgroundImage = `url(${dataUrl})`; document.body.style.backgroundSize = 'cover'; document.body.style.backgroundRepeat = 'no-repeat'; document.body.style.backgroundPosition = 'center center'; } else { document.body.style.backgroundImage = ''; } } function applyHueOverlay(color, intensity) { // Remove old overlay let overlay = document.getElementById('zedHueOverlay'); if (!overlay) { overlay = document.createElement('div'); overlay.id = 'zedHueOverlay'; Object.assign(overlay.style, { pointerEvents: 'none', position: 'fixed', top: '0', left: '0', width: '100%', height: '100%', zIndex: '99999', mixBlendMode: 'hue', transition: 'background-color 0.3s ease', }); document.body.appendChild(overlay); } if (intensity > 0) { overlay.style.backgroundColor = color; overlay.style.opacity = (intensity/100).toString(); overlay.style.display = 'block'; } else { overlay.style.display = 'none'; } } // --- Build menu --- function buildMenu() { menu = el('div', { styles: { position: 'fixed', bottom: '16px', right: '16px', width: '280px', backgroundColor: '#222', color: '#eee', borderRadius: '12px', boxShadow: '0 4px 15px rgba(0,0,0,0.5)', padding: '16px', fontFamily: 'Arial, sans-serif', fontSize: '14px', zIndex: '100000', display: 'flex', flexDirection: 'column', gap: '10px', } }); // Title const title = el('h2', { text: 'Theme Settings', styles: { margin: '0 0 8px 0', fontSize: '18px', fontWeight: '700', textAlign: 'center' } }); menu.appendChild(title); // Theme color picker const colorLabel = el('label', { text: 'Theme Color:', styles: { fontWeight: '700' } }); colorInput = el('input', { attrs: { type: 'color' }, styles: { width: '100%', height: '36px', borderRadius: '6px', border: 'none', cursor: 'pointer' } }); colorInput.value = lsGet(LS_KEYS.themeColor, DEFAULTS.themeColor); menu.appendChild(colorLabel); menu.appendChild(colorInput); // Invert colors checkbox invertCheckbox = el('input', { attrs: { type: 'checkbox' }, styles: { marginRight: '6px', cursor: 'pointer' } }); invertCheckbox.checked = lsGet(LS_KEYS.invertColors, DEFAULTS.invertColors); const invertLabel = el('label', { styles: { cursor: 'pointer', userSelect: 'none', display: 'flex', alignItems: 'center' } }); invertLabel.appendChild(invertCheckbox); invertLabel.appendChild(document.createTextNode('Invert Colors (excluding images)')); menu.appendChild(invertLabel); // Hide logo checkbox hideLogoCheckbox = el('input', { attrs: { type: 'checkbox' }, styles: { marginRight: '6px', cursor: 'pointer' } }); hideLogoCheckbox.checked = lsGet(LS_KEYS.hideLogo, DEFAULTS.hideLogo); const hideLogoLabel = el('label', { styles: { cursor: 'pointer', userSelect: 'none', display: 'flex', alignItems: 'center' } }); hideLogoLabel.appendChild(hideLogoCheckbox); hideLogoLabel.appendChild(document.createTextNode('Hide Logo')); menu.appendChild(hideLogoLabel); // Page title input const pageTitleLabel = el('label', { text: 'Page Title:', styles: { fontWeight: '700' } }); pageTitleInput = el('input', { attrs: { type: 'text', placeholder: 'Enter custom page title' }, styles: { width: '100%', padding: '6px 8px', borderRadius: '6px', border: '1px solid #444', backgroundColor: '#333', color: '#eee', } }); pageTitleInput.value = lsGet(LS_KEYS.pageTitle, DEFAULTS.pageTitle); menu.appendChild(pageTitleLabel); menu.appendChild(pageTitleInput); // Background image upload const bgLabel = el('label', { text: 'Upload Background Image:', styles: { fontWeight: '700', marginTop: '8px' } }); bgInput = el('input', { attrs: { type: 'file', accept: 'image/*' }, styles: { width: '100%' } }); menu.appendChild(bgLabel); menu.appendChild(bgInput); // Hue overlay color picker + intensity hueColorInput = el('input', { attrs: { type: 'color' }, styles: { width: '60%', height: '36px', borderRadius: '6px', border: 'none', cursor: 'pointer' } }); hueColorInput.value = lsGet(LS_KEYS.hueOverlayColor, DEFAULTS.hueOverlayColor); hueIntensityInput = el('input', { attrs: { type: 'range', min: '0', max: '100' }, styles: { width: '35%', verticalAlign: 'middle', accentColor: lsGet(LS_KEYS.themeColor, DEFAULTS.themeColor) } }); hueIntensityInput.value = lsGet(LS_KEYS.hueOverlayIntensity, DEFAULTS.hueOverlayIntensity); const hueWrapper = el('div', { styles: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '8px' } }); hueWrapper.appendChild(hueColorInput); hueWrapper.appendChild(hueIntensityInput); menu.appendChild(el('label', { text: 'Hue Overlay:', styles: { fontWeight: '700', marginTop: '8px' } })); menu.appendChild(hueWrapper); // Reset to default button resetBtn = el('button', { text: 'Reset to Default', styles: { backgroundColor: '#444', color: '#eee', border: 'none', borderRadius: '6px', padding: '10px', cursor: 'pointer', fontWeight: '700', marginTop: '10px', userSelect: 'none', transition: 'background-color 0.2s', }, events: { mouseenter() { this.style.backgroundColor = '#666'; }, mouseleave() { this.style.backgroundColor = '#444'; }, } }); menu.appendChild(resetBtn); // Minimize button (top right inside menu) minimizeBtn = el('button', { text: '−', attrs: { title: 'Minimize Menu', type: 'button' }, styles: { position: 'absolute', top: '10px', right: '12px', width: '28px', height: '28px', fontSize: '20px', lineHeight: '28px', borderRadius: '50%', border: 'none', backgroundColor: colorInput.value, color: '#fff', cursor: 'pointer', userSelect: 'none', boxShadow: '0 2px 6px rgba(0,0,0,0.4)', } }); menu.style.position = 'fixed'; menu.style.paddingTop = '44px'; menu.style.boxSizing = 'border-box'; menu.style.userSelect = 'none'; menu.appendChild(minimizeBtn); // Minimized button (floating gear icon) minimizedBtn = el('button', { text: '⚙', attrs: { title: 'Open Theme Settings', type: 'button' }, styles: { position: 'fixed', bottom: '16px', right: '16px', width: '40px', height: '40px', fontSize: '24px', borderRadius: '50%', border: 'none', backgroundColor: colorInput.value, color: '#fff', cursor: 'pointer', userSelect: 'none', boxShadow: '0 2px 6px rgba(0,0,0,0.4)', display: 'none', zIndex: '100001', } }); document.body.appendChild(minimizedBtn); // Show/hide according to saved state const isMinimized = lsGet(LS_KEYS.menuMinimized, DEFAULTS.menuMinimized); if (isMinimized) { menu.style.display = 'none'; minimizedBtn.style.display = 'block'; } else { menu.style.display = 'flex'; minimizedBtn.style.display = 'none'; } // --- Event listeners --- colorInput.addEventListener('input', () => { const val = colorInput.value; lsSet(LS_KEYS.themeColor, val); applyThemeColor(val); minimizeBtn.style.backgroundColor = val; minimizedBtn.style.backgroundColor = val; hueIntensityInput.style.accentColor = val; }); invertCheckbox.addEventListener('change', () => { const val = invertCheckbox.checked; lsSet(LS_KEYS.invertColors, val); applyInvertColors(val); }); hideLogoCheckbox.addEventListener('change', () => { const val = hideLogoCheckbox.checked; lsSet(LS_KEYS.hideLogo, val); applyHideLogo(val); }); pageTitleInput.addEventListener('input', () => { let val = pageTitleInput.value.trim(); if (!val) val = DEFAULTS.pageTitle; lsSet(LS_KEYS.pageTitle, val); applyPageTitle(val); }); bgInput.addEventListener('change', () => { const file = bgInput.files[0]; if (!file) { lsSet(LS_KEYS.customBackground, ''); applyCustomBackground(''); return; } const reader = new FileReader(); reader.onload = e => { const dataUrl = e.target.result; lsSet(LS_KEYS.customBackground, dataUrl); applyCustomBackground(dataUrl); }; reader.readAsDataURL(file); }); hueColorInput.addEventListener('input', () => { const color = hueColorInput.value; lsSet(LS_KEYS.hueOverlayColor, color); applyHueOverlay(color, Number(hueIntensityInput.value)); }); hueIntensityInput.addEventListener('input', () => { const intensity = Number(hueIntensityInput.value); lsSet(LS_KEYS.hueOverlayIntensity, intensity); applyHueOverlay(hueColorInput.value, intensity); }); resetBtn.addEventListener('click', () => { // Reset all values lsSet(LS_KEYS.themeColor, DEFAULTS.themeColor); lsSet(LS_KEYS.invertColors, DEFAULTS.invertColors); lsSet(LS_KEYS.hideLogo, DEFAULTS.hideLogo); lsSet(LS_KEYS.pageTitle, DEFAULTS.pageTitle); lsSet(LS_KEYS.customBackground, ''); lsSet(LS_KEYS.hueOverlayColor, DEFAULTS.hueOverlayColor); lsSet(LS_KEYS.hueOverlayIntensity, DEFAULTS.hueOverlayIntensity); // Update inputs colorInput.value = DEFAULTS.themeColor; invertCheckbox.checked = DEFAULTS.invertColors; hideLogoCheckbox.checked = DEFAULTS.hideLogo; pageTitleInput.value = DEFAULTS.pageTitle; bgInput.value = ''; hueColorInput.value = DEFAULTS.hueOverlayColor; hueIntensityInput.value = DEFAULTS.hueOverlayIntensity; // Apply all applyThemeColor(DEFAULTS.themeColor); applyInvertColors(DEFAULTS.invertColors); applyHideLogo(DEFAULTS.hideLogo); applyPageTitle(DEFAULTS.pageTitle); applyCustomBackground(''); applyHueOverlay(DEFAULTS.hueOverlayColor, DEFAULTS.hueOverlayIntensity); }); minimizeBtn.addEventListener('click', () => { menu.style.display = 'none'; minimizedBtn.style.display = 'block'; lsSet(LS_KEYS.menuMinimized, true); }); minimizedBtn.addEventListener('click', () => { menu.style.display = 'flex'; minimizedBtn.style.display = 'none'; lsSet(LS_KEYS.menuMinimized, false); }); // Append menu to body document.body.appendChild(menu); } // --- Initial apply of saved settings --- function applyAllSettings() { const themeColor = lsGet(LS_KEYS.themeColor, DEFAULTS.themeColor); const invertColors = lsGet(LS_KEYS.invertColors, DEFAULTS.invertColors); const hideLogo = lsGet(LS_KEYS.hideLogo, DEFAULTS.hideLogo); const pageTitle = lsGet(LS_KEYS.pageTitle, DEFAULTS.pageTitle); const customBg = lsGet(LS_KEYS.customBackground, DEFAULTS.customBackground); const hueColor = lsGet(LS_KEYS.hueOverlayColor, DEFAULTS.hueOverlayColor); const hueIntensity = lsGet(LS_KEYS.hueOverlayIntensity, DEFAULTS.hueOverlayIntensity); applyThemeColor(themeColor); applyInvertColors(invertColors); applyHideLogo(hideLogo); applyPageTitle(pageTitle); applyCustomBackground(customBg); applyHueOverlay(hueColor, hueIntensity); } // --- Observe page title changes dynamically to keep user custom title --- function setupTitleObserver() { const observer = new MutationObserver(() => { const savedTitle = lsGet(LS_KEYS.pageTitle, DEFAULTS.pageTitle); if (document.title !== savedTitle && savedTitle !== DEFAULTS.pageTitle) { document.title = savedTitle; } }); observer.observe(document.querySelector('title') || document.head, { childList: true, subtree: true }); } // --- Initialize --- function init() { buildMenu(); applyAllSettings(); setupTitleObserver(); } window.addEventListener('load', init); })();