kawa extras

Import/Export settings injected as a SigMod tab

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==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);

})();