kici.moe+

Redirects homestuck.com and enhances homestuck.kici.moe with a settings menu.

// ==UserScript==
// @name         kici.moe+
// @namespace    http://tampermonkey.net/
// @version      4.9
// @description  Redirects homestuck.com and enhances homestuck.kici.moe with a settings menu.
// @author       StafkiGTN, Cascade, chris_pie, Unknown
// @license MIT
// @match        *://*.homestuck.com/*
// @match        *://homestuck.kici.moe/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    const KICI_HOSTNAME = 'homestuck.kici.moe';
    const OFFICIAL_DOMAIN_PART = 'homestuck.com';

    // --- Script Mode Router ---
    // This is the most important part of the script. It cleanly separates the two main functions.

    if (window.location.hostname.includes(OFFICIAL_DOMAIN_PART)) {
        // MODE 1: REDIRECTOR
        // If we are on any homestuck.com page, do nothing but redirect.
        const redirectEnabled = GM_getValue('redirector_enabled', true);
        if (redirectEnabled) {
            const currentPath = window.location.pathname + window.location.search;
            window.location.replace(`https://${KICI_HOSTNAME}${currentPath}`);
        }
        // No other code runs in this mode.

    } else if (window.location.hostname.includes(KICI_HOSTNAME)) {
        // MODE 2: ENHANCER
        // If we are on kici.moe, load all the enhancement modules and the settings UI.

        // --- MODULE DEFINITIONS ---
        const modules = {
            'redirector': {
                name: 'Enable Redirection',
                author: 'StafkiGTN',
                description: 'Automatically redirect from homestuck.com to here.',
                enabled: GM_getValue('redirector_enabled', true),
                init: () => {} // This module only affects the other domain.
           },
            'keyboardNav': {
                name: 'Keyboard Navigation',
                author: 'Cascade',
                description: 'Use left/right arrow keys to navigate between pages.',
                enabled: GM_getValue('keyboardNav_enabled', true),
                init: () => {
                    // Pages where keyboard navigation should be disabled
                    const disabledPages = [
                        '/story/2792', '/story/5263', '/story/5368', '/story/3438',
                        '/story/5398', '/story/5427', '/story/8127', '/story/5309',
                    ];

                    // Check if current page is in the disabled list
                    const currentPath = window.location.pathname;
                    if (disabledPages.some(page => currentPath.endsWith(page))) {
                        return; // Don't enable keyboard navigation on these pages
                    }

                    document.addEventListener('keydown', function(e) {
                        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) {
                            return;
                        }
                        let navButton;
                        if (e.key === 'ArrowLeft') {
                            navButton = document.querySelector('a[href*="/story/"][title="Previous page"]');
                            if (!navButton) {
                                navButton = document.querySelector('a[href*="/story/"] > img[alt="Previous page"]');
                                if(navButton) navButton = navButton.parentElement;
                            }
                        } else if (e.key === 'ArrowRight') {
                            navButton = document.querySelector('a[href*="/story/"][title="Next page"]');
                            if (!navButton) {
                                navButton = document.querySelector('a[href*="/story/"] > img[alt="Next page"]');
                                if(navButton) navButton = navButton.parentElement;
                            }
                        }
                        if (navButton && navButton.href) {
                            window.location.href = navButton.href;
                        }
                    });
                }
            },
            'textSizer': {
                name: 'Text Size & Layout',
                author: 'Unknown',
                description: 'Increases text size and readability.',
                enabled: GM_getValue('textSizer_enabled', true),
                init: () => {
                    GM_addStyle(`
                        .o_story-container { max-width: 800px !important; }
                        .type-hs-small--md { font-size: 18px !important; }
                    `);
                }
            },
            'linkFixer': {
                name: 'Fix homestuck.com Links',
                author: 'Cascade & StafkiGTN',
                description: 'Rewrites old links to point to kici.moe.',
                enabled: GM_getValue('linkFixer_enabled', true),
                init: () => {
                    const observer = new MutationObserver(() => {
                        document.querySelectorAll(`a[href*="${OFFICIAL_DOMAIN_PART}"]`).forEach(link => {
                            link.href = link.href.replace(OFFICIAL_DOMAIN_PART, KICI_HOSTNAME);
                        });
                    });
                    observer.observe(document.documentElement, { childList: true, subtree: true });
                }
            },
            'dyslexicFont': {
                name: 'OpenDyslexic Font',
                author: 'StafkiGTN',
                description: 'Uses OpenDyslexic font for all text. (Warning: may ruin immersion in some parts)',
                enabled: GM_getValue('dyslexicFont_enabled', false),
                init: () => {
                    GM_addStyle(`
                        @font-face {
                            font-family: 'OpenDyslexic';
                            src: url('https://file.garden/Zlu9wAzAvyz0wKHn/opendyslexic/OpenDyslexic-Regular.otf') format('opentype');
                        }
                        body, * { font-family: 'OpenDyslexic', sans-serif !important; }
                    `);
                }
            }
        };

        // --- SETTINGS PANEL UI ---
        function createSettingsPanel() {
            GM_addStyle(`
                #kici-plus-settings-panel, .kici-plus-settings-btn-replacement {
                    font-family: 'Courier New', Courier, monospace;
                    font-weight: bold;
                    border-radius: 0 !important;
                }
                #kici-plus-settings-panel { display: none; position: fixed; top: 10px; left: 10px; z-index: 99999; background: #333; color: white; border: 1px solid #666; padding: 15px; max-width: 320px; max-height: 90vh; overflow-y: auto; }
                #kici-plus-settings-panel h3 { margin-top: 0; border-bottom: 1px solid #555; padding-bottom: 5px; }
                #kici-plus-settings-panel label { display: block; margin: 15px 0; cursor: pointer; }
                #kici-plus-settings-panel input { margin-right: 10px; vertical-align: middle; }
                #kici-plus-settings-panel .mod-name { font-weight: bold; }
                #kici-plus-settings-panel .mod-author { font-size: 0.8em; font-weight: normal; color: #ccc; margin-left: 8px; }
                #kici-plus-settings-panel .mod-desc { font-size: 0.9em; color: #ccc; display: block; margin-top: 4px; font-weight: normal; }
                .kici-plus-settings-btn-replacement { background: #B8B8B8; color: white; border: none; padding: 8px 12px; cursor: pointer; font-size: 14px; }
            `);

            const settingsPanel = document.createElement('div');
            settingsPanel.id = 'kici-plus-settings-panel';

            let panelContent = '<h3>kici.moe+ Settings</h3>';
            for (const key in modules) {
                const mod = modules[key];
                panelContent += `
                    <label>
                        <input type="checkbox" id="${key}_cb" ${mod.enabled ? 'checked' : ''}/>
                        <span class="mod-name">${mod.name}</span><span class="mod-author">by ${mod.author}</span>
                        <span class="mod-desc">${mod.description}</span>
                    </label>`;
            }
            settingsPanel.innerHTML = panelContent;
            document.body.appendChild(settingsPanel);

            for (const key in modules) {
                document.getElementById(`${key}_cb`).addEventListener('change', (e) => {
                    GM_setValue(`${key}_enabled`, e.target.checked);
                    window.location.reload();
                });
            }

            const observer = new MutationObserver((mutations, obs) => {
                const footerLogo = document.querySelector('img[src*="footer_logo"]');
                if (footerLogo && !document.querySelector('.kici-plus-settings-btn-replacement')) {
                    const settingsBtn = document.createElement('button');
                    settingsBtn.textContent = 'kici.moe+ Settings';
                    settingsBtn.className = 'kici-plus-settings-btn-replacement';

                    settingsBtn.addEventListener('click', (e) => {
                        e.preventDefault();
                        settingsPanel.style.display = settingsPanel.style.display === 'block' ? 'none' : 'block';
                    });
                    footerLogo.parentNode.replaceChild(settingsBtn, footerLogo);
                    obs.disconnect();
                }
            });
            observer.observe(document.documentElement, { childList: true, subtree: true });
        }

        // --- INITIALIZATION ---
        function initialize() {
            createSettingsPanel();
            for (const key in modules) {
                if (modules[key].enabled) {
                    modules[key].init();
                }
            }
        }

        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', initialize);
        } else {
            initialize();
        }
    }
})();