// ==UserScript== // @name Duolingo Wide // @namespace http://tampermonkey.net/ // @version 1.8.1 // @description Make Duolingo wider and more minimalist, and add useful keyboard shortcuts for the main features of the site. // @author Nekosuki // @match https://www.duolingo.com/* // @grant GM_addStyle // @run-at document-start // @downloadURL none // ==/UserScript== GM_addStyle("div.a5SW0,div._2_lzu{display:none}div._3MT-S{width:100%}div.kHldG{width:20%}"); GM_addStyle("div._2AVC7 > :last-child{display:none} div._2AVC7 > :first-child{width:100%}"); GM_addStyle(".__duowide_achievs > :last-child > ul {max-height:none} .__duowide_friends a+div {position:absolute}"); (function() { "use strict"; let addStrengthenButton = function() { let c = document.querySelector("div.mAsUf"); if(c === null || c.firstElementChild.dataset.test != "lingot-store-button") return; let a = document.querySelector("a[data-test='global-practice']"); a.className = "_3LN9C _3QG2_ _1vaUe _3IS_q _1XnsG _1vaUe _3IS_q"; a.innerHTML = "Practice"; let p = a.cloneNode(true); p.onclick = function() { a.click(); return false; }; c.replaceChild(p, c.firstChild); }; let addAchievementsInProfile = function() { let d = document.querySelector("div.a5SW0"); if(d === null || d.firstChild.textContent != "Achievements") return; let e = d.nextElementSibling; let c = document.querySelector("div._3MT-S").firstChild; d.className = "_2hEQd _1E3L7 __duowide_achievs"; e.className = "_2hEQd _1E3L7 __duowide_friends"; c.insertBefore(e, c.lastChild); c.insertBefore(d, c.lastChild); }; let keyEventListener = function(event) { if(['input', 'select', 'textarea'].indexOf(document.activeElement.tagName.toLowerCase()) !== -1) return; if(event.keyCode == 68 && (window.location.pathname === "/practice" || window.location.pathname.match(/^\/skill\/[^/]+\/[^/]+\//))) { let discussSentence = document.querySelector("div._3rrAo._1RUUp > :last-child :last-child"); if(discussSentence !== null) discussSentence.click(); return; } let tag = null; switch(event.keyCode) { case 78: /* N */ newLearningSession(); return; case 72: /* H */ tag = "home-nav"; break; case 87: /* W */ tag = "vocab-nav"; break; case 68: /* D */ tag = "discussion-nav"; break; case 76: /* L */ tag = "labs-nav"; break; case 80: /* P */ tag = "global-practice"; break; case 89: /* Y */ tag = "user-profile"; break; case 83: /* S */ tag = "sound-settings"; break; case 81: /* Q */ tag = "player-quit"; break; } let elem = document.querySelector("a[data-test='" + tag + "']"); if(elem !== null) elem.click(); else if(window.location.pathname === "/practice") { let [practiceButton, timedPracticeButton] = document.querySelector("div._1cw2r").childNodes; if(event.keyCode == 80) practiceButton.click(); else if(event.keyCode == 84) timedPracticeButton.click(); } }; let checkForSettings = function() { let m = document.querySelector("div._3MT-S"); if(m === null) return; let s = document.querySelector("div._2_lzu"); if(window.location.pathname.startsWith("/settings")) { m.style.width = "auto"; s.style.display = "block"; } else { m.style.width = "100%"; s.style.display = "none"; } }; let init = function() { document.addEventListener("keyup", keyEventListener); }; let check = function() { addAchievementsInProfile(); addStrengthenButton(); checkForSettings(); }; let newLearningSession = function() { if(window.location.pathname.startsWith("/skill/")) { let next = document.querySelector("a[data-test=begin-session-button]"); if(next !== null) next.click(); return; } let skills = Array.prototype.slice.call(document.querySelectorAll("div._2GJb6 > a")); let toLearn = skills.filter(a => a.dataset.test === "blue skill-tree-link" && a.firstElementChild.childElementCount == 1); if(toLearn.length === 0) return; toLearn[0].click(); }; new MutationObserver(check).observe(document, {childList: true, subtree: true}); init(); })();