Last active 1778119947

在办公室使用gpt会被同事追问:你怎么用上的?怎么翻墙的?能给我用用吗?而你又不想给这类 “等人喂饭” 的人群分享时,那么这个脚本就可以帮助到你了

Revision 5c0bc34efaa709a7957eb27d4c84e134f5e11036

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