Adds a the first StackOverflow answer to your search query
Versione datata
// ==UserScript==
// @name Stack Pop
// @namespace https://codeberg.org/happybits/stack-pop
// @version 1.0
// @license MIT
// @description Adds a the first StackOverflow answer to your search query
// @author happybits
// @match https://www.google.com/search*
// @icon
// @grant GM_xmlhttpRequest
// ==/UserScript==
try {
go()
async function go() {
// Wait for the google search to appear in the DOM
const rso = await waitFor("#rso")
// Inject StackPop before the google search result
const stackDiv = document.createElement("div")
stackDiv.id = "stack-pop"
rso.before(stackDiv)
// Find the first search result that leads to Stack Overflow
const googleResults = Array.from(rso.children)
const firstStackOverflowHit = googleResults
.filter(result => result.querySelector("a")?.href)
.map(result => result.querySelector("a").href)
.find(link => link.startsWith("https://stackoverflow.com"))
// If any, get the answer inject it to the search page
if (firstStackOverflowHit) {
// HTTP call using Tampermonkey's built in function
GM_xmlhttpRequest({
method: "GET",
url: firstStackOverflowHit,
onload: function (response) {
// Build a DOM from the text-response and parse the question and the first answer
const stackOverFlowPage = new DOMParser().parseFromString(response.responseText, 'text/html');
const question = stackOverFlowPage.querySelector("h1").textContent
const firstAnswerContent = stackOverFlowPage.querySelector(".answer .s-prose").innerHTML
// Styling for the StackOverflow answer
const stackPopStyling = `
#stack-pop {
width: 700px;
}
#stack-pop li {
margin-left: 1.5em;
}
#stack-pop pre {
background-color: #eee;
padding: 1em;
width: fit-content;
}
#stack-pop code {
background-color: #eee;
}
#stack-pop img {
max-width: 100%;
}
`
// Create the StackPop widget
// Yes I know, it's inline styling etc, but it's by design, I think it's easy to read and compact!
// Or am I wrong ;) ?
stackDiv.innerHTML = `
<style>${stackPopStyling}</style>
<div style="border:solid 2px #f48225; margin: 1em 0; overflow: auto;">
<a href="${firstStackOverflowHit}" style="text-decoration:none">
<div style="cursor:pointer; background-color:#f48225; color:white; padding:0.5em; font-size: 1.5em">
${question}
</div>
</a>
<div style="padding:0 1.5em 1.5em 1.5em;">
${firstAnswerContent}
</div>
</div>
`
}
});
}
}
// This is a generic method that can be used to select element that may take some time to appear in the DOM
// The second parameter "scope" is optional, of you want to limit the query
function waitFor(selector, scope) {
const pause = 10
let maxTime = 10000
return new Promise(resolve => {
function inner() {
if (maxTime <= 0) {
throw "Timeout for select " + selector
}
const element = (scope ?? document).querySelector(selector)
if (element) {
resolve(element)
return
}
maxTime -= pause
setTimeout(inner)
}
inner()
})
}
// A simple log function which shows with a TAMPER-prefix
function log(...message) {
console.log('%c TAMPER ', 'color: white; background-color: #61dbfb', ...message);
}
}
// Unexpected errors is shown in red
catch (exception) {
console.log('%c TAMPER ', 'color: white; background-color: red', exception);
}