Kiwi Farms Highlighted Post Keyboard Shortcuts

Allows you to move between highlighted posts with the A and D keys.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Kiwi Farms Highlighted Post Keyboard Shortcuts
// @namespace    http://kiwifarms.st
// @version      2025-07-27
// @description  Allows you to move between highlighted posts with the A and D keys.
// @author       Enzo
// @match        https://kiwifarms.st/threads/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=kiwifarms.st
// @grant        none
// @license      CC0
// ==/UserScript==

let SelectedPost;
function FindPost(ID) {
    return document.querySelector(`article[data-content="${ID}"]`);
}

function GetPostFromHash() {
    let Hash = window.location.hash;
    if (!Hash) {
        return;
    }

    const ID = Hash.substring(1);
    return FindPost(ID);
}

function ClickHightlightAnchor(Anchor) {
    let NewPost;
    let Href = Anchor.getAttribute('href');
    if (Href) {
        if (Href.startsWith('#')) {
            // If just a hash, make it the selected post.
            const ID = Href.substring(1);
            NewPost = FindPost(ID);
            //window.location.hash = Href; // Update page hash as well.
        } else {
            NewPost = true;//Prevents us from falling back to the global buttons.
        }
        Anchor.click();
    } else {
        // Probably at the very start of end, so do nothing.
        return true;
    }

    return NewPost;
}

function ClickPostHighlight(PostElement, Direction) {
    let QueryString = 'a.hb-react-highlight';
    if (Direction < 0) { QueryString += 'Prev'; }
    else if (Direction > 0) { QueryString += 'Next'; }
    else { return; }

    const Anchor = PostElement.querySelector(QueryString);
    if (!Anchor) {
        console.error(`Unable to locate ${Direction < 0 ? "previous": "next"} highlight anchor.`, PostElement);
        return;
    }

    return ClickHightlightAnchor(Anchor);
}

function ChangeSelectedPost(New) {
    if (SelectedPost) {
        const Former = SelectedPost.querySelector('div.message-cell--main');
        Former.style.background = null;
    }
    SelectedPost = New;
    if (New) {
        const NewMain = SelectedPost.querySelector('div.message-cell--main');
        NewMain.style.background = '#0002';
    }
}

function DoMove(Dir) {
    if (SelectedPost) {
        const NewPost = ClickPostHighlight(SelectedPost, Dir);
        if (NewPost) {
            if (NewPost !== true) {// Redirect to another page.
                console.log('New Selected Post from existing;', SelectedPost, '->', NewPost);
                ChangeSelectedPost(NewPost);
            }
            return;
        }
    }

    // No current selected post. Fall back to the core highlight buttons.
    const BlockOuter = document.querySelector('div.block-outer-opposite');
    let ClassName = 'i.fa-award';
    if (Dir < 0) {
        ClassName = 'i.fa-backward';
    }
    const IEle = BlockOuter.querySelector(ClassName);
    if (!IEle) {
        console.error(`Unable to locate global ${Dir < 0 ? "previous" : "next"} highlight class;`, ClassName);
        return;
    }

    const ParentAnchor = IEle.closest('a');// This causes a side effect once you're on pages after the final highlight. It will take you back to the final highlight. More of a feature than a bug.
    if (!ParentAnchor) {
        console.error(`Unable to locate global ${Dir < 0 ? "previous" : "next"} highlight anchor. Reached;`, ParentAnchor);
        return;
    }

    const NewPost = ClickHightlightAnchor(ParentAnchor);
    if (NewPost && NewPost !== true) {
        console.log('New Selected Post from global highlight buttons;', NewPost);
        ChangeSelectedPost(NewPost);
    }
}

function DoKeyDown(e) {
    let Dir = 0;
    switch (e.code) {
        case 'KeyA'://Back
            Dir = -1;
            break;
        case 'KeyD'://Forward
            Dir = 1;
            break;
        default:
            return;
    }

    DoMove(Dir);
}

(function() {
    'use strict';
    ChangeSelectedPost(GetPostFromHash());
    if (SelectedPost) {
        console.log('OnLoad Selected Post', SelectedPost);
    }

    document.addEventListener('keydown', DoKeyDown, false);
})();