kawa extras

Import/Export settings injected as a SigMod tab

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

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

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

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.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name         kawa extras
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Import/Export settings injected as a SigMod tab
// @author       kawasaki
// @license      MIT
// @match        *://one.sigmally.com/*
// @icon         https://i.imgur.com/U6RhZAt.png
// @grant        none
// @run-at       document-idle
// ==/UserScript==
(function() {
    'use strict';

    let excludeBinds = localStorage.getItem('se_exclude_binds') === 'true';

    // ── known sigfix keybind keys to strip when excluding binds ──
    const SIGFIX_BIND_KEYS = [
        'multibox','respawnKey','horizontalLineKey','verticalLineKey',
        'fixedLineKey','doubleKey','tripleKey','quadKey','rapidFeedKey',
        'splitKey','feedKey','macroSplitKey','lineSplitKey',
    ];

    function exportSettings() {
        const out = { version: '1.0', timestamp: new Date().toISOString() };

        // SigMod
        try {
            const raw = localStorage.getItem('SigModClient-settings');
            if (raw) {
                let d = JSON.parse(raw);
                if (excludeBinds) {
                    d = JSON.parse(JSON.stringify(d));
                    if (d.macros)   { d.macros.keys  = {}; d.macros.mouse = {}; }
                    if (d.keybinds) { d.keybinds = {}; }
                }
                out.sigmod = { key: 'SigModClient-settings', data: d };
            }
        } catch(e) {}

        // SigFix
        try {
            const raw = localStorage.getItem('sigfix');
            if (raw) {
                let d = JSON.parse(raw);
                if (excludeBinds) {
                    d = JSON.parse(JSON.stringify(d));
                    SIGFIX_BIND_KEYS.forEach(k => delete d[k]);
                }
                out.sigfix = { key: 'sigfix', data: d };
            }
        } catch(e) {}


        const blob = new Blob([JSON.stringify(out, null, 2)], { type: 'application/json' });
        const url  = URL.createObjectURL(blob);
        const a    = document.createElement('a');
        a.href     = url;
        a.download = `sigmally-settings-${Date.now()}.json`;
        a.click();
        URL.revokeObjectURL(url);
        setStatus('✓ Exported!', '#4caf50');
    }

    function importSettings(file) {
        const reader = new FileReader();
        reader.onload = (e) => {
            try {
                const data = JSON.parse(e.target.result);

                if (data.sigmod?.key && data.sigmod?.data) {
                    localStorage.setItem(data.sigmod.key, JSON.stringify(data.sigmod.data));
                    try { if (window.sigmod?.settings) Object.assign(window.sigmod.settings, data.sigmod.data); } catch(e) {}
                }

                if (data.sigfix?.key && data.sigfix?.data) {
                    localStorage.setItem(data.sigfix.key, JSON.stringify(data.sigfix.data));
                    try {
                        if (window.sigfix?.settings) {
                            Object.assign(window.sigfix.settings, data.sigfix.data);
                            window.sigfix.settings.refresh?.();
                        }
                    } catch(e) {}
                }

                setStatus('✓ Applied! Some settings may need a reload.', '#4caf50');
            } catch(err) {
                setStatus('✗ Invalid file.', '#f44336');
            }
        };
        reader.readAsText(file);
    }

    function setStatus(msg, color) {
        const el = document.getElementById('se-status');
        if (!el) return;
        el.textContent  = msg;
        el.style.color  = color;
        setTimeout(() => { el.textContent = ''; }, 4000);
    }

    // ── Tab content HTML ──
    const tabHTML = `
        <div id="se-tab-content" class="mod_tab scroll" style="display:none;opacity:0;flex-direction:column;gap:14px;padding:14px;font-family:Ubuntu,Arial,sans-serif;color:#e0e0ff;box-sizing:border-box;">

            <!-- header -->
            <div style="display:flex;align-items:center;gap:10px;padding-bottom:10px;border-bottom:1px solid #1a1a2e;">
                <img src="https://i.imgur.com/U6RhZAt.png" style="width:28px;height:28px;border-radius:6px;flex-shrink:0;" />
                <div>
                    <div style="font-size:14px;font-weight:bold;color:#aaccff;">kawa extras</div>
                    <div style="font-size:11px;color:#556677;">Import &amp; Export your settings</div>
                </div>
            </div>

            <!-- export card -->
            <div style="background:#0a0a0a;border:1px solid #1e1e1e;border-radius:10px;padding:12px;display:flex;flex-direction:column;gap:8px;">
                <div style="font-size:12px;font-weight:bold;color:#aaccff;">Export Settings</div>
                <div style="font-size:11px;color:#556677;line-height:1.5;">
                    Saves all your SigMod, Sigmally Fixes and other settings to a JSON file you can restore later.
                </div>

                <!-- exclude binds checkbox -->
                <label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:12px;color:#aaa;padding:6px 8px;background:#111;border-radius:7px;border:1px solid #222;">
                    <input type="checkbox" id="se-exclude-binds" ${excludeBinds ? 'checked' : ''} style="width:14px;height:14px;cursor:pointer;accent-color:#5a44eb;" />
                    Exclude keybinds from export
                </label>

                <button id="se-export-btn" style="
                    background:#1b3a5c;border:1px solid #336699;border-radius:7px;
                    color:#fff;padding:9px;cursor:pointer;font-size:13px;
                    font-family:Ubuntu,Arial,sans-serif;transition:opacity 0.15s;
                ">⬆ Export Settings</button>
            </div>

            <!-- import card -->
            <div style="background:#0a0a0a;border:1px solid #1e1e1e;border-radius:10px;padding:12px;display:flex;flex-direction:column;gap:8px;">
                <div style="font-size:12px;font-weight:bold;color:#aaccff;">Import Settings</div>
                <div style="font-size:11px;color:#556677;line-height:1.5;">
                    Load a previously exported JSON file. Settings are applied live where possible.
                </div>
                <label id="se-import-label" style="
                    background:#1b3a5c;border:1px solid #336699;border-radius:7px;
                    color:#fff;padding:9px;cursor:pointer;font-size:13px;
                    text-align:center;display:block;font-family:Ubuntu,Arial,sans-serif;
                    transition:opacity 0.15s;
                ">
                    ⬇ Import Settings
                    <input type="file" id="se-import-input" accept=".json" style="display:none;" />
                </label>
            </div>

            <!-- what gets saved -->
            <div style="background:#0a0a0a;border:1px solid #1e1e1e;border-radius:10px;padding:10px;font-size:11px;color:#556677;line-height:1.8;">
                ✓ &nbsp;SigMod settings &nbsp;·&nbsp; ✓ &nbsp;Sigmally Fixes settings
            </div>

            <span id="se-status" style="font-size:12px;text-align:center;min-height:16px;"></span>
        </div>
    `;

    // ── CSS ──
    const css = `
        #se-tab-content button:hover,
        #se-import-label:hover {
            opacity: 0.82;
        }
        #se-tab-content .mod_tab {
            /* prevent inheriting unwanted mod_tab overrides */
        }
    `;

    function injectTab() {
        const nav     = document.querySelector('.mod_menu_navbar');
        const content = document.querySelector('.mod_menu_content');
        if (!nav || !content) return false;
        if (document.getElementById('se-nav-btn')) return true;

        // inject CSS
        const styleEl = document.createElement('style');
        styleEl.textContent = css;
        document.head.appendChild(styleEl);

        // ── nav button ──
        const navBtn = document.createElement('button');
        navBtn.id        = 'se-nav-btn';
        navBtn.className = 'mod_nav_btn';
        navBtn.style.cssText = 'display:flex;align-items:center;justify-content:center;gap:5px;width:100%;box-sizing:border-box;line-height:1;padding-top:0;padding-bottom:0;';
        navBtn.innerHTML = `
            <img src="https://i.imgur.com/U6RhZAt.png"
                 style="width:16px;height:16px;border-radius:3px;flex-shrink:0;display:block;margin:0;" />
            <span style="line-height:1;display:block;">Sig extras</span>
        `;
        nav.appendChild(navBtn);

        // ── tab div ──
        const tabWrapper = document.createElement('div');
        tabWrapper.innerHTML = tabHTML;
        content.appendChild(tabWrapper);
        const tabDiv = document.getElementById('se-tab-content');

        // nav button click
        navBtn.addEventListener('click', () => {
            // hide all other tabs
            document.querySelectorAll('.mod_tab').forEach(t => {
                t.style.opacity = '0';
                setTimeout(() => { if (t !== tabDiv) t.style.display = 'none'; }, 200);
            });
            // deselect all nav buttons
            document.querySelectorAll('.mod_nav_btn').forEach(b => b.classList.remove('mod_selected'));
            navBtn.classList.add('mod_selected');

            setTimeout(() => {
                tabDiv.style.display = 'flex';
                setTimeout(() => { tabDiv.style.opacity = '1'; }, 10);
            }, 200);
        });

        // ── wire up controls ──

        document.getElementById('se-exclude-binds').addEventListener('change', (e) => {
            excludeBinds = e.target.checked;
            localStorage.setItem('se_exclude_binds', excludeBinds);
        });

        document.getElementById('se-export-btn').addEventListener('click', exportSettings);

        document.getElementById('se-import-input').addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (file) importSettings(file);
            e.target.value = '';
        });

        return true;
    }

    // poll until SigMod nav is ready
    const interval = setInterval(() => {
        if (injectTab()) clearInterval(interval);
    }, 300);

})();