-
Notifications
You must be signed in to change notification settings - Fork 1
/
Stack Overflow - UI Tweaks.user.js
286 lines (258 loc) · 10.8 KB
/
Stack Overflow - UI Tweaks.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
// ==UserScript==
// @name Stack Overflow - UI Tweaks
// @namespace https://github.com/adamhotep/userscripts
// @description Blue<->red user badges by reputation, wide code, hide flagged Qs
// @include https://stackoverflow.com/*
// @include https://serverfault.com/*
// @include https://superuser.com/*
// @include https://meta.stackoverflow.com/*
// @include https://meta.serverfault.com/*
// @include https://meta.superuser.com/*
// @include https://*.stackexchange.com/*
// @include https://askubuntu.com/*
// @include https://meta.askubuntu.com/*
// @include https://answers.onstartups.com/*
// @include https://meta.answers.onstartups.com/*
// @include https://mathoverflow.net/*
// @include http://stackoverflow.com/*
// @include http://serverfault.com/*
// @include http://superuser.com/*
// @include http://meta.stackoverflow.com/*
// @include http://meta.serverfault.com/*
// @include http://meta.superuser.com/*
// @include http://*.stackexchange.com/*
// @include http://askubuntu.com/*
// @include http://meta.askubuntu.com/*
// @include http://answers.onstartups.com/*
// @include http://meta.answers.onstartups.com/*
// @include http://mathoverflow.net/*
// @require https://github.com/adamhotep/nofus.js/raw/main/nofus.js
// @version 1.4.20241121.0
// @author Adam Katz
// @grant none
// ==/UserScript==
// Copyright (C) 2016+ by Adam Katz, https://stackexchange.com/users/674651
// Licensed under the GPL v3+ {{{
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// Beerware: If you think this is worth it, you are welcome to buy me a beer.
// The author of this script is also open to different licensing models in
// order to facilitate incorporation into StackExchange properties.
// }}}
// Stylesheet for tweaks
var style = nf.style$('/* Stack Overflow - UI Tweaks */\n');
function addStyle(css) { style.textContent += css; }
// color user badges by how high their score is {{{
function rep2color(rep) {
let hue = Math.log2(rep);
let saturation = 60;
// 2^17 = 131k (top 711 all-time as of Jun 2022), so we'll make that 100% red
// and then turn up saturation for top performers like Jon Skeet (#1).
// Skeet's 1336k gets us 93%. Note 999% is treated as 100%, so no cap needed.
// Math.SE (2nd biggest SE site)'s #1 is 588k, Superuser (3rd)'s #1 is 404k
if (hue > 17) {
saturation = Math.round(saturation + (hue - 17) * 10);
hue = 17;
}
hue = Math.round(hue / 17 * 180 + 180);
return `hsla(${hue}, ${saturation}%, 50%, 0.2)`;
}
// questions and answers
var user_info = qa$(`.user-info`);
if (user_info) {
addStyle(`
.reputation_color { text-shadow: 0 0 .1em white; border-radius:.6em; }
.reputation_color:hover {
background-color:transparent!important;
transition:background-color 1s;
}
.post-signature { border:2px solid transparent; }
.owner { border-radius:.6em; border-color:#88f; }
`);
}
for (let u = 0, ul = user_info.length; u < ul; u++) {
let box = user_info[u].parentElement;
// badge pages shouldn't go to the parent
if (box.classList.contains("single-badge-count")) {
box = user_info[u];
}
let rep = q$(`.reputation-score`, box);
if (! rep) { continue; }
if (rep.title && rep.title.match(/score\s[0-9][0-9,]+/))
rep = rep.title;
else rep = rep.innerText;
rep = rep.replace(/[^0-9]+/g, "");
box.style.backgroundColor = rep2color(rep);
box.classList.add("reputation_color");
}
// indexes and search results
var usrcmin = "s-user-card__minimal";
var user_rep = qa$(`.${usrcmin} [title^="reputation score"]`);
if (user_rep) {
addStyle(`
.${usrcmin} {
/* attr(foo, color) is too new: https://bugzil.la/1448251 */
/* background-color: attr(data-bgcolor, color, transparent); */
padding:0.3em 0.5em !important; border-radius:0.6em;
text-shadow: 0 0 0.1em white;
}
.${usrcmin}:hover {
background-color: transparent!important; transition:background-color 1s;
}
`);
}
for (let u = 0, ul = user_rep.length; u < ul; u++) {
let rep = user_rep[u].innerText / 1;
if (isNaN(rep)) { continue; }
let card = user_rep[u].parentElement;
while (!card.classList.contains(usrcmin)) { card = card.parentElement; }
// browsers don't yet implement attr(foo, color): https://bugzil.la/1448251
// card.dataset.bgcolor = rep2color(rep);
card.style.backgroundColor = rep2color(rep);
}
// done coloring user badges by score }}}
// code blocks: hover to widen, clickable comments links {{{
if(q$('#question, .answer, pre')) {
// Denote whether shift is being held
let onKeyDown = function(event) {
if (event.key == "Shift") { document.body.classList.add("shift_key"); }
};
let onKeyUp = function(event) {
if (event.key == "Shift") { document.body.classList.remove("shift_key"); }
};
window.addEventListener("keydown", onKeyDown, false);
window.addEventListener("keyup", onKeyUp, false);
let noshift = /* syn=css */ `body:not(.shift_key)`; // CSS matcher
// CSS to widen on hover
addStyle(`
/* comments in links */
${noshift} pre.s-code-block > code a:not(:hover) .hljs-comment {
color:rgb(from var(--highlight-comment, #667)
r g calc(b + 60)) !important;
}
${noshift} pre.s-code-block > code a:hover .hljs-comment {
color:rgb(from var(--highlight-comment, #667)
calc(r - 60) calc(g - 60) calc(b + 180)) !important;
}
${noshift} pre.code_block.wider:hover {
background-color:#eeee; /* a tiny bit of transparency */
position:relative; z-index:9; /* don't disrupt later elements */
/* This previously used overflow-x:scroll but box-sizing:border-box fails
* to account for the scrollbar even though it was previously present,
* so we use overflow-x:hidden instead. This "shouldn't" matter */
overflow-x:hidden; /* don't move later elements up by scrollbar height */
box-sizing: border-box;
width:-moz-fit-content; width:-webkit-fit-content; width:fit-content;
/* BUG: this breaks on window resizes, wontfix */
max-width:${document.body.clientWidth}px;
}
pre.code_block.wider.widest {
position:relative;
}
${noshift} pre.code_block.wider.widest:hover {
left:0!important; /* enable offset correction in js code */
overflow-x:auto; /* this MIGHT require scroll and/or !important */
z-index:1001; /* On top of .left-sidebar { z-index:1000 } */
}
body {
/* added Linux-friendly fonts ahead of the defaults.
* (a July 2021 SE change used a font whose spaces were too narrow) */
--ff-mono: Hack,"Panic Sans","Bitstream Vera Sans Mono",Inconsolata,
"Droid Sans Mono",ui-monospace,"Cascadia Mono","Segoe UI Mono",
"Liberation Mono",Menlo,Monaco,Consolas,monospace;
}
`); // fix syntax highlighting: `
// Designate which code blocks need to grow and by how much
nf.wait$('div.post-text pre, div.s-prose pre', code_block => {
code_block.classList.add("code_block");
let width = code_block.scrollWidth;
let offset = code_block.getBoundingClientRect().x;
if (width && width > code_block.clientWidth) {
code_block.classList.add("wider");
if (offset + width > document.body.clientWidth) {
code_block.classList.add("widest");
// marginLeft and marginRight shift everything to the left edge
// left moves it back to the right, except on :hover (see CSS)
// therefore the element will be full window width
code_block.style.marginRight = offset + "px"; // shifted
code_block.style.marginLeft = -offset + "px"; // - offset
code_block.style.left = + offset + "px"; // + offset
}
}
// Make links clickable.
code_block.innerHTML = code_block.innerHTML.replace(
// avoid (non-HTML-escaped) ampersands, tags, and trailing punctuation
/(?!<[\w.-])https?:\/\/[-.\w]+\.\w{2,9}\b(?:[^&\s<>]+(?:&)*)+[^\s;?.!,<>()\[\]{}'"&]/ig,
'<a href="$&">$&</a>');
});
}
// Done with code blocks }}}
// collapse flagged questions when viewing > 15 questions {{{
// (closed, on hold, dupe, etc) https://meta.stackexchange.com/q/10582/259816
var questions = qa$(`#questions h3 a[href^="/questions"]`);
var add_question_css = false;
for (let q = 0, ql = questions.length; ql > 15 && q < ql; q++) {
if (questions[q].innerText.match(
/\[(?:duplicate|on hold|migrated|closed)\]$/
)) {
let expander = document.createElement("a");
add_question_css = true;
expander.href = questions[q].href;
expander.appendChild(document.createTextNode(" "));
expander.classList.add("expander");
// must be added *before* the text since the text might wrap
questions[q].parentElement.insertBefore(expander, questions[q]);
questions[q].parentElement.parentElement.parentElement
.classList.add("skip");
expander.onclick = function() {
this.parentElement.parentElement.parentElement.classList.toggle("open");
return false; // don't actually go anywhere
};
}
}
if (add_question_css) {
let qs = "#questions .skip";
let closed_link = `${qs}:not(.open) a.question-hyperlink`;
addStyle(`
${qs}:not(.open) { height:3.5rem; white-space:nowrap; overflow:clip; }
${qs}:not(.open) [class*="-stats"] { overflow-x:clip; }
${qs} .expander::before { color:#59c; }
${closed_link}:not(:hover) { color:#abc; }
${closed_link} { letter-spacing:-0.02em;
font-family:Arial Narrow,Carlito,Calibri; }
${qs}:not(.open) .expander::before { content:"(expand)"; cursor:zoom-in; }
${qs}.open .expander::before { content:"(shrink)"; cursor:zoom-out; }
`);
}
// Done collapsing flagged questions }}}
// adblock {{{
var sponsored = qa$(`div.site-header--sponsored`);
for (let s=0, sl=sponsored.length; s < sl; s++) {
sponsored[s].parentElement.style.setProperty('display', 'none', 'important');
}
// }}}
// Misc CSS tweaks {{{
addStyle(`
.deleted-answer pre, .deleted-answer pre code {
background-color:var(--black-050);
}
.s-table-container .s-table td { padding:1px 1ex; }
textarea.wmd-input {
/* we have to guess the scrollbar width :-( FF w/ GTK = 24px for me */
width:calc(80ch + 24px); /* 80 monospace chars + scrollbar */
scrollbar-gutter:stable; /* always allocate space for scrollbar */
/* that widening has overlap issues with the sidebar. render atop: */
position:relative; z-index:5;
}
`); // Done with misc CSS tweaks }}}