This commit is contained in:
Brian 2025-04-19 00:56:00 -04:00
parent cde2fd3331
commit 25fe0da7cb
No known key found for this signature in database
4 changed files with 120 additions and 73 deletions

View File

@ -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 = "";
})
}

91
keyboard-watcher.user.js Normal file
View File

@ -0,0 +1,91 @@
// ==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.js
// @updateURL https://gitea.amine-bouabdallaoui.fr/AmineB/ycombinator-keys/raw/branch/main/keyboard-watcher.js
// ==/UserScript==
// jshint esversion: 11
let selected = -1
globalThis.document.styleSheets[0].insertRule('tbody tr.athing>td.title>span.titleline>a:focus,.athing.comtr a.togg.clicky:focus{outline: none}')
const requery = () => globalThis.document.querySelectorAll('tbody tr.athing')
let query = requery()
const
// This is a Firefox only option. For some reason older versions of Firefox can't set outline: none.
focusInvisible = { __proto__: null, focusVisible: false },
highlightSelected = () => {
query[selected].style.boxShadow = '0px 0px 10px 4px rgba(0,0,0,0.73)'
globalThis.location.pathname === '/item' ?
// 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()
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 'u':
globalThis.open(globalThis.location.pathname === '/item' ? query[selected].querySelector('a.hnuser').href : query[selected].nextSibling.querySelector('a.hnuser').href, '_self')
return
case 'U':
globalThis.open(globalThis.location.pathname === '/item' ? query[selected].querySelector('a.hnuser').href : 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 })

View File

@ -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": "2",
"description": "Navigate ycombinator with your keyboard",
"icons": {
"48": "icons/48.png"
},
"content_scripts": [
{
"matches": [
"*://news.ycombinator.com/*"
],
"js": [
"keyboard-watcher.js"
]
}
],
"browser_specific_settings": {
"gecko": {
"id": "{8baac415-d606-422e-89a6-2122747c54dc}"
}
}
}

7
web-ext-config.mjs Normal file
View File

@ -0,0 +1,7 @@
export default {
__proto__: null,
ignoreFiles: [
"README.md",
"LICENSE"
]
}