change-gpt-logo.user.js
· 7.0 KiB · JavaScript
Bruto
// ==UserScript==
// @name ChatGPT Logo 换成 DeepSeek
// @namespace https://gist.doracoin.cc/doracoin/39fd3ec77d3044fc95b408bdf6c0ac13
// @version 2026.05.07
// @license MIT
// @description 替换 ChatGPT 左上角 sidebar-header 的 SVG logo 为 DeepSeek logo,并实现 #page-header 向上收缩(垂直折叠),支持 SPA 重绘、状态记忆,并增加可配置项与主题适配按钮。
// @author Doracoin <[email protected]>
// @homepageURL https://gist.doracoin.cc/doracoin/39fd3ec77d3044fc95b408bdf6c0ac13
// @updateURL https://gist.doracoin.cc/doracoin/39fd3ec77d3044fc95b408bdf6c0ac13/raw/HEAD/change-gpt-logo.user.js
// @downloadURL https://gist.doracoin.cc/doracoin/39fd3ec77d3044fc95b408bdf6c0ac13/download/HEAD/change-gpt-logo.user.js
// @match https://chatgpt.com/*
// @icon https://deepseek.com/favicon.ico
// @run-at document-body
// @grant GM_addStyle
// ==/UserScript==
(function () {
'use strict';
// ====== 配置区 ======
const CONFIG = {
deepseekLogoUrl: 'https://commons.wikimedia.org/wiki/Special:Redirect/file/DeepSeek_logo.svg',
deepseekSquareIconUrl: 'https://deepseek.com/favicon.ico',
storageKey: 'deepseek_header_collapsed_v2',
headerMaxHeight: 82,
buttonSize: 36,
buttonPosition: { top: 10, right: 12 },
enableLogs: false
};
// ====================
// 动态样式,支持深色/浅色主题自动适配
GM_addStyle(`
#page-header {
--ds-header-max-height: ${CONFIG.headerMaxHeight}px;
max-height: var(--ds-header-max-height);
overflow: hidden;
transition: max-height 260ms ease, padding 260ms ease;
}
#page-header.collapsed {
max-height: 0 !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
#page-header.collapsed * {
visibility: hidden;
pointer-events: none;
}
#ds-page-header-toggle {
position: fixed;
top: ${CONFIG.buttonPosition.top}px;
right: ${CONFIG.buttonPosition.right}px;
z-index: 2147483647;
display: inline-flex;
align-items: center;
justify-content: center;
width: ${CONFIG.buttonSize}px;
height: ${CONFIG.buttonSize}px;
border-radius: 6px;
font-size: 16px;
line-height: 1;
background: var(--ds-btn-bg, rgba(0,0,0,0.6));
color: var(--ds-btn-fg, #fff);
cursor: pointer;
user-select: none;
transition: transform 140ms ease, opacity 140ms ease, background 200ms ease;
box-shadow: 0 6px 18px rgba(0,0,0,0.35);
margin-top: 38px;
}
#ds-page-header-toggle:hover {
transform: translateY(-2px);
}
@media (prefers-color-scheme: light) {
#ds-page-header-toggle {
--ds-btn-bg: rgba(255,255,255,0.85);
--ds-btn-fg: #000;
}
}
@media (prefers-color-scheme: dark) {
#ds-page-header-toggle {
--ds-btn-bg: rgba(0,0,0,0.6);
--ds-btn-fg: #fff;
}
}
`);
function log(...args) {
if (CONFIG.enableLogs) console.log('[DeepSeek]', ...args);
}
function replaceLogo() {
try {
const sidebarHeader = document.querySelector('#sidebar-header');
if (!sidebarHeader) return;
if (sidebarHeader.querySelector('img[data-deepseek-logo]')) return;
const logoLink = sidebarHeader.querySelector('a[aria-label="主页"]');
if (!logoLink) return;
const img = document.createElement('img');
img.setAttribute('src', CONFIG.deepseekLogoUrl);
img.setAttribute('alt', 'DeepSeek');
img.setAttribute('data-deepseek-logo', '1');
img.style.height = '28px';
img.style.width = 'auto';
img.style.objectFit = 'contain';
img.style.verticalAlign = 'middle';
logoLink.innerHTML = '';
logoLink.appendChild(img);
if (logoLink.classList.contains("w-9")) {
logoLink.classList.remove("w-9");
logoLink.classList.add("w-21");
}
log('logo replaced');
} catch (e) {
log('replaceLogo error', e);
}
}
function replaceCollapsedIcon() {
try {
const openBtn = document.querySelector('button[aria-label="打开边栏"]');
if (!openBtn) return;
const firstSvg = openBtn.querySelector('svg');
if (!firstSvg) return;
if (openBtn.querySelector('img[data-deepseek-collapsed]')) return;
const img = document.createElement('img');
img.setAttribute('src', CONFIG.deepseekSquareIconUrl);
img.setAttribute('alt', 'DeepSeek');
img.setAttribute('data-deepseek-collapsed', '1');
img.style.width = '20px';
img.style.height = '20px';
img.style.objectFit = 'contain';
firstSvg.replaceWith(img);
log('collapsed icon replaced');
} catch (e) {
log('replaceCollapsedIcon error', e);
}
}
function ensureToggle() {
let btn = document.getElementById('ds-page-header-toggle');
if (btn) return btn;
btn = document.createElement('div');
btn.id = 'ds-page-header-toggle';
btn.setAttribute('role', 'button');
btn.setAttribute('aria-pressed', 'false');
btn.setAttribute('title', '折叠/展开页眉 (DeepSeek)');
btn.innerText = '▲';
btn.addEventListener('click', () => toggleHeader());
document.body.appendChild(btn);
return btn;
}
function getHeader() {
return document.querySelector('#page-header') || document.querySelector('header') || document.querySelector('.page-header');
}
function toggleHeader(forceState) {
const header = getHeader();
if (!header) return;
const shouldCollapse = typeof forceState === 'boolean' ? forceState : !header.classList.contains('collapsed');
header.classList.toggle('collapsed', shouldCollapse);
try { localStorage.setItem(CONFIG.storageKey, shouldCollapse ? '1' : '0'); } catch (e) {}
const btn = ensureToggle();
btn.innerText = shouldCollapse ? '▼' : '▲';
btn.setAttribute('aria-pressed', shouldCollapse ? 'true' : 'false');
}
function restoreState() {
try {
const v = localStorage.getItem(CONFIG.storageKey);
if (v === '1') toggleHeader(true);
} catch (e) {}
}
let debounceTimer = null;
function onDomChanged() {
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
replaceLogo();
replaceCollapsedIcon();
ensureToggle();
restoreState();
}, 250);
}
const observer = new MutationObserver(onDomChanged);
observer.observe(document.body, { childList: true, subtree: true });
replaceLogo();
replaceCollapsedIcon();
ensureToggle();
restoreState();
window.addEventListener('load', () => setTimeout(onDomChanged, 300));
})();
| 1 | // ==UserScript== |
| 2 | // @name ChatGPT Logo 换成 DeepSeek |
| 3 | // @namespace https://gist.doracoin.cc/doracoin/39fd3ec77d3044fc95b408bdf6c0ac13 |
| 4 | // @version 2026.05.07 |
| 5 | // @license MIT |
| 6 | // @description 替换 ChatGPT 左上角 sidebar-header 的 SVG logo 为 DeepSeek logo,并实现 #page-header 向上收缩(垂直折叠),支持 SPA 重绘、状态记忆,并增加可配置项与主题适配按钮。 |
| 7 | // @author Doracoin <[email protected]> |
| 8 | // @homepageURL https://gist.doracoin.cc/doracoin/39fd3ec77d3044fc95b408bdf6c0ac13 |
| 9 | // @updateURL https://gist.doracoin.cc/doracoin/39fd3ec77d3044fc95b408bdf6c0ac13/raw/HEAD/change-gpt-logo.user.js |
| 10 | // @downloadURL https://gist.doracoin.cc/doracoin/39fd3ec77d3044fc95b408bdf6c0ac13/download/HEAD/change-gpt-logo.user.js |
| 11 | // @match https://chatgpt.com/* |
| 12 | // @icon https://deepseek.com/favicon.ico |
| 13 | // @run-at document-body |
| 14 | // @grant GM_addStyle |
| 15 | // ==/UserScript== |
| 16 | |
| 17 | (function () { |
| 18 | 'use strict'; |
| 19 | |
| 20 | // ====== 配置区 ====== |
| 21 | const CONFIG = { |
| 22 | deepseekLogoUrl: 'https://commons.wikimedia.org/wiki/Special:Redirect/file/DeepSeek_logo.svg', |
| 23 | deepseekSquareIconUrl: 'https://deepseek.com/favicon.ico', |
| 24 | storageKey: 'deepseek_header_collapsed_v2', |
| 25 | headerMaxHeight: 82, |
| 26 | buttonSize: 36, |
| 27 | buttonPosition: { top: 10, right: 12 }, |
| 28 | enableLogs: false |
| 29 | }; |
| 30 | // ==================== |
| 31 | |
| 32 | // 动态样式,支持深色/浅色主题自动适配 |
| 33 | GM_addStyle(` |
| 34 | #page-header { |
| 35 | --ds-header-max-height: ${CONFIG.headerMaxHeight}px; |
| 36 | max-height: var(--ds-header-max-height); |
| 37 | overflow: hidden; |
| 38 | transition: max-height 260ms ease, padding 260ms ease; |
| 39 | } |
| 40 | #page-header.collapsed { |
| 41 | max-height: 0 !important; |
| 42 | padding-top: 0 !important; |
| 43 | padding-bottom: 0 !important; |
| 44 | } |
| 45 | #page-header.collapsed * { |
| 46 | visibility: hidden; |
| 47 | pointer-events: none; |
| 48 | } |
| 49 | |
| 50 | #ds-page-header-toggle { |
| 51 | position: fixed; |
| 52 | top: ${CONFIG.buttonPosition.top}px; |
| 53 | right: ${CONFIG.buttonPosition.right}px; |
| 54 | z-index: 2147483647; |
| 55 | display: inline-flex; |
| 56 | align-items: center; |
| 57 | justify-content: center; |
| 58 | width: ${CONFIG.buttonSize}px; |
| 59 | height: ${CONFIG.buttonSize}px; |
| 60 | border-radius: 6px; |
| 61 | font-size: 16px; |
| 62 | line-height: 1; |
| 63 | background: var(--ds-btn-bg, rgba(0,0,0,0.6)); |
| 64 | color: var(--ds-btn-fg, #fff); |
| 65 | cursor: pointer; |
| 66 | user-select: none; |
| 67 | transition: transform 140ms ease, opacity 140ms ease, background 200ms ease; |
| 68 | box-shadow: 0 6px 18px rgba(0,0,0,0.35); |
| 69 | margin-top: 38px; |
| 70 | } |
| 71 | #ds-page-header-toggle:hover { |
| 72 | transform: translateY(-2px); |
| 73 | } |
| 74 | |
| 75 | @media (prefers-color-scheme: light) { |
| 76 | #ds-page-header-toggle { |
| 77 | --ds-btn-bg: rgba(255,255,255,0.85); |
| 78 | --ds-btn-fg: #000; |
| 79 | } |
| 80 | } |
| 81 | @media (prefers-color-scheme: dark) { |
| 82 | #ds-page-header-toggle { |
| 83 | --ds-btn-bg: rgba(0,0,0,0.6); |
| 84 | --ds-btn-fg: #fff; |
| 85 | } |
| 86 | } |
| 87 | `); |
| 88 | |
| 89 | function log(...args) { |
| 90 | if (CONFIG.enableLogs) console.log('[DeepSeek]', ...args); |
| 91 | } |
| 92 | |
| 93 | function replaceLogo() { |
| 94 | try { |
| 95 | const sidebarHeader = document.querySelector('#sidebar-header'); |
| 96 | if (!sidebarHeader) return; |
| 97 | |
| 98 | if (sidebarHeader.querySelector('img[data-deepseek-logo]')) return; |
| 99 | |
| 100 | const logoLink = sidebarHeader.querySelector('a[aria-label="主页"]'); |
| 101 | if (!logoLink) return; |
| 102 | |
| 103 | const img = document.createElement('img'); |
| 104 | img.setAttribute('src', CONFIG.deepseekLogoUrl); |
| 105 | img.setAttribute('alt', 'DeepSeek'); |
| 106 | img.setAttribute('data-deepseek-logo', '1'); |
| 107 | img.style.height = '28px'; |
| 108 | img.style.width = 'auto'; |
| 109 | img.style.objectFit = 'contain'; |
| 110 | img.style.verticalAlign = 'middle'; |
| 111 | |
| 112 | logoLink.innerHTML = ''; |
| 113 | logoLink.appendChild(img); |
| 114 | |
| 115 | if (logoLink.classList.contains("w-9")) { |
| 116 | logoLink.classList.remove("w-9"); |
| 117 | logoLink.classList.add("w-21"); |
| 118 | } |
| 119 | |
| 120 | log('logo replaced'); |
| 121 | } catch (e) { |
| 122 | log('replaceLogo error', e); |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | function replaceCollapsedIcon() { |
| 127 | try { |
| 128 | const openBtn = document.querySelector('button[aria-label="打开边栏"]'); |
| 129 | if (!openBtn) return; |
| 130 | |
| 131 | const firstSvg = openBtn.querySelector('svg'); |
| 132 | if (!firstSvg) return; |
| 133 | |
| 134 | if (openBtn.querySelector('img[data-deepseek-collapsed]')) return; |
| 135 | |
| 136 | const img = document.createElement('img'); |
| 137 | img.setAttribute('src', CONFIG.deepseekSquareIconUrl); |
| 138 | img.setAttribute('alt', 'DeepSeek'); |
| 139 | img.setAttribute('data-deepseek-collapsed', '1'); |
| 140 | img.style.width = '20px'; |
| 141 | img.style.height = '20px'; |
| 142 | img.style.objectFit = 'contain'; |
| 143 | |
| 144 | firstSvg.replaceWith(img); |
| 145 | |
| 146 | log('collapsed icon replaced'); |
| 147 | } catch (e) { |
| 148 | log('replaceCollapsedIcon error', e); |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | function ensureToggle() { |
| 153 | let btn = document.getElementById('ds-page-header-toggle'); |
| 154 | if (btn) return btn; |
| 155 | |
| 156 | btn = document.createElement('div'); |
| 157 | btn.id = 'ds-page-header-toggle'; |
| 158 | btn.setAttribute('role', 'button'); |
| 159 | btn.setAttribute('aria-pressed', 'false'); |
| 160 | btn.setAttribute('title', '折叠/展开页眉 (DeepSeek)'); |
| 161 | btn.innerText = '▲'; |
| 162 | btn.addEventListener('click', () => toggleHeader()); |
| 163 | document.body.appendChild(btn); |
| 164 | return btn; |
| 165 | } |
| 166 | |
| 167 | function getHeader() { |
| 168 | return document.querySelector('#page-header') || document.querySelector('header') || document.querySelector('.page-header'); |
| 169 | } |
| 170 | |
| 171 | function toggleHeader(forceState) { |
| 172 | const header = getHeader(); |
| 173 | if (!header) return; |
| 174 | |
| 175 | const shouldCollapse = typeof forceState === 'boolean' ? forceState : !header.classList.contains('collapsed'); |
| 176 | header.classList.toggle('collapsed', shouldCollapse); |
| 177 | |
| 178 | try { localStorage.setItem(CONFIG.storageKey, shouldCollapse ? '1' : '0'); } catch (e) {} |
| 179 | |
| 180 | const btn = ensureToggle(); |
| 181 | btn.innerText = shouldCollapse ? '▼' : '▲'; |
| 182 | btn.setAttribute('aria-pressed', shouldCollapse ? 'true' : 'false'); |
| 183 | } |
| 184 | |
| 185 | function restoreState() { |
| 186 | try { |
| 187 | const v = localStorage.getItem(CONFIG.storageKey); |
| 188 | if (v === '1') toggleHeader(true); |
| 189 | } catch (e) {} |
| 190 | } |
| 191 | |
| 192 | let debounceTimer = null; |
| 193 | function onDomChanged() { |
| 194 | if (debounceTimer) clearTimeout(debounceTimer); |
| 195 | debounceTimer = setTimeout(() => { |
| 196 | replaceLogo(); |
| 197 | replaceCollapsedIcon(); |
| 198 | ensureToggle(); |
| 199 | restoreState(); |
| 200 | }, 250); |
| 201 | } |
| 202 | |
| 203 | const observer = new MutationObserver(onDomChanged); |
| 204 | observer.observe(document.body, { childList: true, subtree: true }); |
| 205 | |
| 206 | replaceLogo(); |
| 207 | replaceCollapsedIcon(); |
| 208 | ensureToggle(); |
| 209 | restoreState(); |
| 210 | window.addEventListener('load', () => setTimeout(onDomChanged, 300)); |
| 211 | })(); |
| 212 |