From 92fd73cd2d2bd6809056b396942ed95a51670fef Mon Sep 17 00:00:00 2001 From: Brian Date: Sat, 19 Apr 2025 12:45:15 -0400 Subject: [PATCH] Rewrite --- keyboard-watcher.js | 54 -------------------- keyboard-watcher.user.js | 103 +++++++++++++++++++++++++++++++++++++++ manifest.json | 41 ++++++++-------- web-ext-config.mjs | 7 +++ 4 files changed, 132 insertions(+), 73 deletions(-) delete mode 100644 keyboard-watcher.js create mode 100644 keyboard-watcher.user.js create mode 100644 web-ext-config.mjs diff --git a/keyboard-watcher.js b/keyboard-watcher.js deleted file mode 100644 index af165c4..0000000 --- a/keyboard-watcher.js +++ /dev/null @@ -1,54 +0,0 @@ -let selectedId = -1; - -document.onkeypress = function(evt) { - evt = evt || window.event; - var charCode = evt.keyCode || evt.which; - var charStr = String.fromCharCode(charCode); - switch(charStr) { - case 'j': - handlePrevious() - selectedId = selectedId + 1; - highlightSelected(); - break; - case 'k': - if (selectedId > 0) { - handlePrevious() - selectedId = selectedId - 1; - } - highlightSelected(); - break; - case 'h': - document.querySelectorAll(".itemlist tbody tr td.subtext .clicky")[selectedId].click(); - highlightSelected(); - break - case 'r': - location.reload() - case 'o': - targetBlankClickAndReset(document.querySelectorAll(".itemlist tbody tr.athing td.title a")[selectedId]) - break; - case 'c': - targetBlankClickAndReset(document.querySelectorAll(".itemlist tbody tr td.subtext a:not(.clicky):not(.hnuser)")[selectedId]); - break; - } -}; - -function highlightSelected() { - if (selectedId != -1) { - document.querySelectorAll(".itemlist tbody tr.athing")[selectedId].style.boxShadow = "0px 0px 10px 4px rgba(0,0,0,0.73)"; - } -} - -function handlePrevious() { - if (selectedId != -1) { - document.querySelectorAll(".itemlist tbody tr.athing")[selectedId].style.boxShadow = "initial"; - } -} - -function targetBlankClickAndReset(el) { - el.target = "_blank"; - setTimeout(() => { - el.click(); - el.target = ""; - }) -} - diff --git a/keyboard-watcher.user.js b/keyboard-watcher.user.js new file mode 100644 index 0000000..cc89a22 --- /dev/null +++ b/keyboard-watcher.user.js @@ -0,0 +1,103 @@ +// ==UserScript== +// @name ycombinator-keys +// @namespace https://gitea.amine-bouabdallaoui.fr/AmineB/ycombinator-keys +// @license gpl-3.0-only +// @match http*://news.ycombinator.com/* +// @icon https://gitea.amine-bouabdallaoui.fr/AmineB/ycombinator-keys/raw/branch/main/icons/48.png +// @grant none +// @version 4 +// @author AmineB +// @description Ycombinator keyboard nav. +// @downloadURL https://gitea.amine-bouabdallaoui.fr/AmineB/ycombinator-keys/raw/branch/main/keyboard-watcher.user.js +// @updateURL https://gitea.amine-bouabdallaoui.fr/AmineB/ycombinator-keys/raw/branch/main/keyboard-watcher.user.js +// ==/UserScript== +// jshint esversion: 11 + +globalThis.document.styleSheets[0].insertRule('tbody tr.athing>td.title>span.titleline>a:focus,tbody tr.athing.comtr a.togg.clicky:focus{outline: none}') + +const requery = () => globalThis.document.querySelectorAll('tbody tr.athing') +let + query = requery(), + selected = -1 +const + // This is a Firefox only option. For some reason older versions of Firefox can't set outline: none. + focusInvisible = { __proto__: null, focusVisible: false }, + isComment = () => query[selected].classList.contains('comtr'), + highlightSelected = () => { + query[selected].style.boxShadow = '0px 0px 10px 4px rgba(0,0,0,0.73)' + + isComment() ? + // Drawing the boxShadow on this selector isn't visible enough. + query[selected].querySelector('a.togg.clicky')?.focus(focusInvisible) + : + // Focuses link without re-query. + query[selected].lastChild.firstChild.firstChild.focus(focusInvisible) + }, + keydown = event => { + switch (event.key) { + case 'j': + if (selected + 1 === query.length) + return + + if (selected !== -1) + query[selected].style.boxShadow = '' + + while (selected < query.length && query[++selected].classList.contains('noshow')); + highlightSelected() + return + case 'k': + if (selected <= 0) + return + + query[selected].style.boxShadow = '' + while (selected >= 0 && query[--selected].classList.contains('noshow')); + highlightSelected() + return + case 'h': + if (globalThis.location.pathname === '/hidden') + for (const selector of query[selected].nextSibling.querySelectorAll('a')) + if (selector.innerText === 'un-hide') { + selector.click() + break + } + else + query[selected].nextSibling.querySelector('a.clicky.hider')?.click() + if (selected + 1 <= query.length) + highlightSelected() + return + case 'c': + globalThis.open('https://news.ycombinator.com/item?id=' + query[selected].id, '_self') + return + case 'C': + globalThis.open('https://news.ycombinator.com/item?id=' + query[selected].id) + return + case 'r': + if (!isComment()) + return + globalThis.open(query[selected].querySelector('div.reply a').href, '_self') + return + case 'R': + if (!isComment()) + return + globalThis.open(query[selected].querySelector('div.reply a').href) + return + case 'u': + globalThis.open((isComment() ? query[selected] : query[selected].nextSibling).querySelector('a.hnuser').href, '_self') + return + case 'U': + globalThis.open((isComment() ? query[selected] : query[selected].nextSibling).querySelector('a.hnuser').href) + } + }, + listen = () => globalThis.document.addEventListener('keydown', keydown) + +listen() + +// This allows for compatibility with automatic pagination scripts. +new globalThis.MutationObserver(mutations => { + for (const mutation of mutations) + if (mutation.type === 'childList') { + globalThis.document.removeEventListener('keydown', keydown) + query = requery() + return listen() + } +}).observe(globalThis.document.body, { __proto__: null, childList: true, subtree: true }) diff --git a/manifest.json b/manifest.json index ef35987..4898dcb 100644 --- a/manifest.json +++ b/manifest.json @@ -1,21 +1,24 @@ { - - "manifest_version": 2, - "name": "Ycombinator keyboard navigation", - "version": "1", - - "description": "Navigate ycombinator with your keyboard", - - "icons": { - "48": "icons/48.png" - }, - - "content_scripts": [ - { - "matches": ["*://*.ycombinator.com/*"], - "js": ["keyboard-watcher.js"] - } - ] - + "manifest_version": 3, + "name": "Ycombinator keyboard navigation", + "version": "4", + "description": "Navigate ycombinator with your keyboard", + "icons": { + "48": "icons/48.png" + }, + "content_scripts": [ + { + "matches": [ + "*://news.ycombinator.com/*" + ], + "js": [ + "keyboard-watcher.user.js" + ] + } + ], + "browser_specific_settings": { + "gecko": { + "id": "{8baac415-d606-422e-89a6-2122747c54dc}" + } + } } - diff --git a/web-ext-config.mjs b/web-ext-config.mjs new file mode 100644 index 0000000..507d664 --- /dev/null +++ b/web-ext-config.mjs @@ -0,0 +1,7 @@ +export default { + __proto__: null, + ignoreFiles: [ + "README.md", + "LICENSE" + ] +}