Import/Export settings injected as a SigMod tab
// ==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 & 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;">
✓ SigMod settings · ✓ 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);
})();