Greasy Fork is available in English.
修复V13.0中误删壁纸的bug,保留输入框不透明+智能悬浮球+侧边栏透明
// ==UserScript==
// @name Gemini 美化 - V13.1
// @namespace http://tampermonkey.net/
// @version 13.1
// @description 修复V13.0中误删壁纸的bug,保留输入框不透明+智能悬浮球+侧边栏透明
// @author You
// @match https://gemini.google.com/*
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// --- 配置 ---
const DEFAULT_BG = "https://w.wallhaven.cc/full/wq/wallhaven-wqery6.jpg";
// --- 1. CSS 样式 ---
const cssContent = `
/* 背景层 */
#custom-bg-layer {
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
z-index: -1;
background-size: cover; background-position: center; background-repeat: no-repeat;
pointer-events: none;
}
/* 全局透明 */
body, html { background-color: transparent !important; }
/* 内容区透明 */
main, .main-container, scroll-view, .chat-history-container, .ql-editor {
background-color: rgba(255, 255, 255, 0.15) !important;
backdrop-filter: blur(0px);
}
/* 侧边栏通用匹配 */
nav, aside, [role="navigation"],
[class*="navigation"], [class*="sidebar"], [class*="drawer"] {
background-color: transparent !important;
background: transparent !important;
border-right: none !important;
}
/* --- 底部去白线 vs 输入框还原 --- */
/* 1. 消除底部容器背景 */
footer, header,
[class*="footer"], [class*="bottom-container"],
[class*="gradient"], [class*="scrim"], [class*="mask"] {
background: transparent !important;
background-image: none !important;
border: none !important;
box-shadow: none !important;
}
/* 2. 恢复输入框不透明 (浅灰/深灰) */
[class*="input-area"], [class*="InputArea"], .input-area-container,
[class*="text-input"], [role="textbox"], .rich-textarea {
background-color: #f0f4f9 !important; /* 浅色模式 */
border-radius: 24px !important;
opacity: 1 !important;
}
*::before, *::after { background-image: none !important; }
/* 暗黑模式适配 */
@media (prefers-color-scheme: dark) {
main, .main-container { background-color: rgba(0, 0, 0, 0.3) !important; }
/* 暗黑模式输入框 */
[class*="input-area"], [class*="InputArea"], .input-area-container,
[class*="text-input"], [role="textbox"], .rich-textarea {
background-color: #1e1f20 !important;
color: #e3e3e3 !important;
}
}
/* --- 面板样式 --- */
#ui-settings-panel {
position: fixed; top: 20px; right: 20px; width: 260px;
background: rgba(0, 0, 0, 0.85); color: #fff;
padding: 16px; border-radius: 12px;
z-index: 999999; font-family: sans-serif; font-size: 13px;
border: 1px solid rgba(255,255,255,0.15);
box-shadow: 0 10px 40px rgba(0,0,0,0.6);
display: flex; flex-direction: column; gap: 12px;
transition: opacity 0.2s;
}
#ui-minimized-icon {
position: fixed; top: 20px; right: 20px;
width: 44px; height: 44px;
background: rgba(0, 0, 0, 0.8);
border: 2px solid rgba(255,255,255,0.3);
border-radius: 50%;
color: white;
display: flex; align-items: center; justify-content: center;
cursor: grab; z-index: 999999;
box-shadow: 0 4px 15px rgba(0,0,0,0.4);
user-select: none; font-size: 20px;
}
.panel-row { display: flex; flex-direction: column; gap: 6px; }
.panel-label-row { display: flex; justify-content: space-between; color: #ccc; font-size: 12px; font-weight: 500;}
.panel-input { width: 100%; cursor: pointer; accent-color: #4a90e2; }
.panel-text { width: 100%; padding: 8px; border-radius: 6px; border: 1px solid #444; background: #222; color: #fff; box-sizing: border-box; font-size: 12px;}
.panel-btn { width: 100%; padding: 10px; background: #4a90e2; border: none; border-radius: 6px; color: white; cursor: pointer; font-weight: bold; }
.panel-header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #444; padding-bottom: 8px; margin-bottom: 4px; }
.panel-title { font-size: 14px; font-weight: bold; color: white; }
.panel-min-btn { cursor: pointer; padding: 2px 8px; font-size: 18px; color: #aaa; border-radius: 4px; line-height: 1; }
`;
const style = document.createElement('style');
style.textContent = cssContent;
document.head.appendChild(style);
// --- 2. 纯 DOM 构建工具 ---
function createEl(tag, props = {}, children = []) {
const el = document.createElement(tag);
Object.entries(props).forEach(([key, val]) => {
if (key === 'style' && typeof val === 'object') {
Object.assign(el.style, val);
} else if (key === 'textContent') {
el.textContent = val;
} else {
el[key] = val;
}
});
children.forEach(child => {
if (typeof child === 'string') el.appendChild(document.createTextNode(child));
else el.appendChild(child);
});
return el;
}
// --- 3. 核心逻辑 ---
function init() {
let bgLayer = document.getElementById('custom-bg-layer');
if (!bgLayer) {
bgLayer = document.createElement('div');
bgLayer.id = 'custom-bg-layer';
const savedUrl = localStorage.getItem('gemini_bg_url') || DEFAULT_BG;
bgLayer.style.backgroundImage = `url(${savedUrl})`;
bgLayer.style.filter = 'blur(0px)';
document.body.appendChild(bgLayer);
}
if (document.getElementById('ui-settings-panel')) return;
const minIcon = createEl('div', { id: 'ui-minimized-icon', textContent: '🎨', style: { display: 'none' } });
document.body.appendChild(minIcon);
const titleText = createEl('span', { className: 'panel-title', textContent: '🎨 美化 (V13.1 修复版)' });
const minBtn = createEl('span', { className: 'panel-min-btn', textContent: '—', title: '最小化' });
const header = createEl('div', { className: 'panel-header' }, [titleText, minBtn]);
// 模糊度
const blurLabel = createEl('span', { textContent: '背景模糊' });
const blurVal = createEl('span', { textContent: '0.0px', style: { color: '#4a90e2' } });
const blurInput = createEl('input', { type: 'range', className: 'panel-input', min: '0', max: '10', step: '0.1', value: '0' });
blurInput.addEventListener('input', (e) => {
bgLayer.style.filter = `blur(${e.target.value}px)`;
blurVal.textContent = e.target.value + 'px';
});
// 透明度
const opLabel = createEl('span', { textContent: '界面白底浓度' });
const opVal = createEl('span', { textContent: '15%', style: { color: '#4a90e2' } });
const opInput = createEl('input', { type: 'range', className: 'panel-input', min: '0', max: '100', step: '1', value: '15' });
opInput.addEventListener('input', (e) => {
opVal.textContent = e.target.value + '%';
updateOpacity(e.target.value);
});
const urlInput = createEl('input', { type: 'text', className: 'panel-text', placeholder: '输入图片直链...' });
const saveBtn = createEl('button', { className: 'panel-btn', textContent: '应用' });
saveBtn.addEventListener('click', () => {
if (urlInput.value) {
localStorage.setItem('gemini_bg_url', urlInput.value);
bgLayer.style.backgroundImage = `url(${urlInput.value})`;
}
});
const panel = createEl('div', { id: 'ui-settings-panel' }, [
header,
createEl('div', { className: 'panel-row' }, [createEl('div', { className: 'panel-label-row' }, [blurLabel, blurVal]), blurInput]),
createEl('div', { className: 'panel-row' }, [createEl('div', { className: 'panel-label-row' }, [opLabel, opVal]), opInput]),
createEl('div', { className: 'panel-row' }, [urlInput, saveBtn])
]);
document.body.appendChild(panel);
// 交互逻辑
minBtn.addEventListener('click', () => {
panel.style.display = 'none';
minIcon.style.display = 'flex';
});
let isDragging = false, offsetX, offsetY;
minIcon.addEventListener('click', () => {
if (!isDragging) {
const iconRect = minIcon.getBoundingClientRect();
minIcon.style.display = 'none';
panel.style.display = 'flex';
const panelWidth = 260;
const panelHeight = 350;
const screenW = window.innerWidth;
const screenH = window.innerHeight;
let newLeft, newTop;
if (iconRect.left > screenW / 2) newLeft = iconRect.left - panelWidth - 15;
else newLeft = iconRect.right + 15;
newTop = iconRect.top;
if (newTop + panelHeight > screenH) newTop = screenH - panelHeight - 20;
if (newTop < 20) newTop = 20;
if (newLeft < 10) newLeft = 10;
if (newLeft + panelWidth > screenW) newLeft = screenW - panelWidth - 10;
panel.style.top = newTop + 'px';
panel.style.left = newLeft + 'px';
panel.style.right = 'auto';
}
});
minIcon.addEventListener('mousedown', (e) => {
isDragging = false; const rect = minIcon.getBoundingClientRect();
offsetX = e.clientX - rect.left; offsetY = e.clientY - rect.top;
document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp);
});
function onMouseMove(e) {
isDragging = true; e.preventDefault();
let l = e.clientX - offsetX; let t = e.clientY - offsetY;
l = Math.max(0, Math.min(l, window.innerWidth - 44));
t = Math.max(0, Math.min(t, window.innerHeight - 44));
minIcon.style.left = l + 'px'; minIcon.style.top = t + 'px'; minIcon.style.right = 'auto';
}
function onMouseUp() { document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); setTimeout(() => isDragging = false, 100); }
}
// --- 4. 智能猎人 (修复Bug版) ---
function smartCleaner(opacityVal = 15) {
const allDivs = document.body.getElementsByTagName('div');
const screenHeight = window.innerHeight;
const screenWidth = window.innerWidth;
const op = opacityVal / 100;
for (let div of allDivs) {
// 【核心修复】白名单:绝不误删壁纸层和面板
if (div.id === 'custom-bg-layer' || div.id === 'ui-settings-panel' || div.id === 'ui-minimized-icon') continue;
// 也不要误删我们的面板内部元素
if (div.closest && div.closest('#ui-settings-panel')) continue;
const rect = div.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) continue;
const style = window.getComputedStyle(div);
const hasBg = (style.backgroundColor !== 'rgba(0, 0, 0, 0)' && style.backgroundColor !== 'transparent') || (style.backgroundImage !== 'none');
if (!hasBg) continue;
// 1. 侧边栏主体
if (rect.height > screenHeight * 0.8 && rect.left < 50 && rect.width < 400) {
div.style.setProperty('background-color', `rgba(255, 255, 255, ${op})`, 'important');
div.style.setProperty('background', `rgba(255, 255, 255, ${op})`, 'important');
}
// 2. 底部全宽白线条 (Footer)
// 必须排除输入框!输入框通常高度 < 100,但 Footer 可能也是。
// 输入框通常 class 包含 input, textarea 等。
// 但这里我们用位置判断:Footer container 通常是全屏宽度的
if (rect.width > screenWidth * 0.9 && rect.bottom > screenHeight - 100) {
div.style.setProperty('background', 'transparent', 'important');
div.style.setProperty('border', 'none', 'important');
}
// 3. 角落渐变遮罩 (左上 + 左下)
const isTopLeft = rect.top < 100 && rect.left < 300;
const isBottomLeft = rect.bottom > screenHeight - 100 && rect.left < 300;
// 增加宽度限制,防止误删输入框 (输入框宽度通常 > 500)
if ((isTopLeft || isBottomLeft) && rect.height < 150 && rect.width < 300) {
div.style.setProperty('background', 'transparent', 'important');
div.style.setProperty('background-image', 'none', 'important');
div.style.setProperty('box-shadow', 'none', 'important');
div.style.setProperty('border', 'none', 'important');
}
}
}
function updateOpacity(val) {
const opacity = val / 100;
const id = 'dynamic-op-style';
let style = document.getElementById(id);
if (!style) { style = document.createElement('style'); style.id = id; document.head.appendChild(style); }
style.textContent = `
main, .main-container, nav, aside, [role="navigation"] {
background-color: rgba(255, 255, 255, ${opacity}) !important;
}
@media (prefers-color-scheme: dark) {
main, nav, aside { background-color: rgba(0, 0, 0, ${opacity}) !important; }
}
`;
smartCleaner(val);
}
setTimeout(() => {
init();
updateOpacity(15);
setInterval(() => {
const opInput = document.querySelector('input[type="range"][max="100"]');
const val = opInput ? opInput.value : 15;
smartCleaner(val);
}, 800);
}, 1500);
})();