kawa extras

Import/Export settings injected as a SigMod tab

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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);

})();