WorkshopEnhance

一個簡單的工作坊網址替換腳本,為網址添加 searchtext=<標題>

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

Advertisement:

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

Advertisement:

// ==UserScript==
// @name WorkshopEnhance
// @version 2026/06/24-Beta
// @author Canaan HS
// @description 一個簡單的工作坊網址替換腳本,為網址添加 searchtext=<標題>
// @description:zh-TW 一個簡單的工作坊網址替換腳本,為網址添加 searchtext=<標題>
// @description:zh-CN 一个简单的工作坊网址替换脚本,为网址添加 searchtext=<标题>
// @description:en A simple workshop URL replacement script that adds searchtext=<title> to the URL

// @license MPL-2.0
// @match *://steamcommunity.com/*
// @icon https://cdn-icons-png.flaticon.com/512/220/220608.png

// @noframes
// @grant window.onurlchange

// @run-at document-start
// @namespace https://greasyfork.org/users/989635
// ==/UserScript==

(() => {
    const app = /^https:\/\/steamcommunity\.com\/app\/\d+/;
    const workshop = /^https:\/\/steamcommunity\.com\/workshop\/browse\/\?appid=\d+/;
    const sharedfiles = /^https:\/\/steamcommunity\.com\/sharedfiles\/filedetails\/\?id=\d+/;
    const myworkshopfiles = /^https:\/\/steamcommunity\.com\/profiles\/\d+\/myworkshopfiles\/?.*$/;

    let jumpMark = false;

    function main(url) {
        if (app.test(url)) {
            waitElem("div[style*='--gap: var(--spacing-9);'] div.Panel", findUri)
        }
        else if (workshop.test(url) || myworkshopfiles.test(url)) {
            waitElem("div[style*='--gap: var(--spacing-5);']", container => {
                waitLoad(container, 300, () => {
                    findUri();
                    waitElem("button[data-accent-color='accent']", buttons => {
                        if (buttons.length === 2) {
                            // 當跳轉標記為 true 時,代表先前觸發過,因此移除先前鍵盤監聽
                            if (jumpMark) window.removeEventListener("keydown", triggerTurnPage);

                            jumpMark = false;
                            window.addEventListener(
                                "keydown",
                                event => triggerTurnPage(event, buttons),
                                { capture: true }
                            );
                        }
                    }, true)
                })
            })
        }
        else if (sharedfiles.test(url)) {
            waitElem(".workshopItemTitle", title => reUri(url, title?.textContent ?? ""));
        }
    };

    function _debounce(func, delay) {
        let timer = null;
        return (...args) => {
            clearTimeout(timer);
            timer = setTimeout(() => {
                func(...args);
            }, delay);
        }
    };

    function findUri() {
        const links = document.querySelectorAll("a[href^='https://steamcommunity.com/sharedfiles/filedetails/?id=']:not([fixed='true'])");
        if (links.length % 2 === 0) {
            for (let i = 0; i < links.length; i += 2) {
                const [rawLink, titleLink] = [links[i], links[i + 1]];
                const titleText = titleLink.textContent.trim();

                if (!titleText) continue;

                reUri(rawLink, titleText);
                reUri(titleLink, titleText);
            }
        }
    };

    function reUri(uri, title) {
        const isElement = uri instanceof Element;
        const Uri = isElement ? uri.href : uri;

        const url = new URL(Uri);
        url.searchParams.set("searchtext", title);

        const newUri = url.href.replace(/\+/g, " ");

        isElement
            ? uri.tagName === "A" ? (uri.href = newUri, uri.setAttribute("fixed", "true")) : null
            : history.replaceState(null, '', newUri);
    };

    const turnPage = (button) => {
        jumpMark = true;
        button.click();
    };
    function triggerTurnPage(event, buttons) {
        const key = event.key;
        if (key === "ArrowLeft" && !jumpMark) {
            turnPage(buttons[0]);
        } else if (key === "ArrowRight" && !jumpMark) {
            turnPage(buttons[1]);
        }
    };

    function waitLoad(container, debounce, run) {
        const observer = new MutationObserver(_debounce(() => {
            observer.disconnect();
            run();
        }, debounce));
        observer.observe(container, { subtree: true, childList: true, attributes: true, characterData: true });
    };

    function waitElem(selector, found, all = false) {
        let timer, idleCallback;

        const query = () => {
            const result = all
                ? document.querySelectorAll(selector)
                : document.querySelector(selector);

            if (all ? result.length > 0 : result) {
                cancelIdleCallback(idleCallback);
                // clearTimeout(timer);
                found(result);
            } else {
                idleCallback = requestIdleCallback(query, { timeout: 5e2 });
            }
        }

        idleCallback = requestIdleCallback(query, { timeout: 5e2 });
        // timer = setTimeout(() => {
        // cancelIdleCallback(idleCallback);
        // }, 2e4);
    };

    function onUrlChange(callback, timeout = 15) {
        let timer;
        let cleaned = false;
        let support_urlchange = false;

        const originalPushState = history.pushState;
        const originalReplaceState = history.replaceState;

        const eventHandler = {
            urlchange: () => trigger('urlchange'),
            popstate: () => trigger('popstate'),
            hashchange: () => trigger('hashchange')
        };

        function trigger(type) {
            clearTimeout(timer);
            if (!support_urlchange && type === 'urlchange') support_urlchange = true;
            timer = setTimeout(() => {
                if (support_urlchange) off(false, true);
                callback({
                    type: type,
                    url: location.href,
                    domain: location.hostname
                });
            }, Math.max(15, timeout));
        };

        function off(all = true, clean = false) {
            if (clean && cleaned) return;

            clearTimeout(timer);
            history.pushState = originalPushState;
            history.replaceState = originalReplaceState;
            window.removeEventListener('popstate', eventHandler.popstate);
            window.removeEventListener('hashchange', eventHandler.hashchange);
            all && window.removeEventListener('urlchange', eventHandler.urlchange);

            cleaned = true;
        };

        window.addEventListener('urlchange', eventHandler.urlchange);
        window.addEventListener('popstate', eventHandler.popstate);
        window.addEventListener('hashchange', eventHandler.hashchange);
        history.pushState = function () {
            originalPushState.apply(this, arguments);
            trigger('pushState');
        };
        history.replaceState = function () {
            originalReplaceState.apply(this, arguments);
            trigger('replacestate');
        };

        return { off };
    };

    // 初次執行
    main(location.href);
    // 監聽網址變化
    onUrlChange(change => {
        main(change.url);
    });
})();