Floating nav buttons with drag, edit, hotkeys, backup, and persistent minimize.
// ==UserScript==
// @name Torn Custom Navigation Buttons
// @namespace http://tampermonkey.net/
// @version 1.6
// @description Floating nav buttons with drag, edit, hotkeys, backup, and persistent minimize.
// @author duckybeks
// @match https://www.torn.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
var STORAGE_KEY_BUTTONS = 'torn_custom_buttons';
var STORAGE_KEY_POS = 'torn_custom_buttons_pos';
var STORAGE_KEY_BACKUP = 'torn_custom_buttons_backup';
var STORAGE_KEY_MINIMIZED = 'torn_nav_minimized';
var DEFAULT_BUTTONS = [
{ id: 1, label: 'Home', url: 'https://www.torn.com/index.php', hotkey: 'H' },
{ id: 2, label: 'Bazaar', url: 'https://www.torn.com/bazaar.php', hotkey: 'B' },
{ id: 3, label: 'Item Market', url: 'https://www.torn.com/page.php?sid=ItemMarket#/market/view=category&categoryName=Most%20Popular', hotkey: 'I' },
{ id: 4, label: 'Trades', url: 'https://www.torn.com/trade.php', hotkey: 'T' },
{ id: 5, label: 'Events', url: 'https://www.torn.com/events.php', hotkey: 'E' },
];
var buttons = loadButtons();
var nextId = buttons.length > 0 ? Math.max(buttons.map(function(b) { return b.id; })) + 1 : 1;
var container = null;
var isDragging = false;
function loadButtons() {
try {
var saved = localStorage.getItem(STORAGE_KEY_BUTTONS);
if (saved) {
var parsed = JSON.parse(saved);
if (Array.isArray(parsed) && parsed.length > 0) return parsed;
}
} catch (e) {}
try {
var backup = localStorage.getItem(STORAGE_KEY_BACKUP);
if (backup) {
var parsed = JSON.parse(backup);
if (Array.isArray(parsed) && parsed.length > 0) {
localStorage.setItem(STORAGE_KEY_BUTTONS, backup);
return parsed;
}
}
} catch (e) {}
return JSON.parse(JSON.stringify(DEFAULT_BUTTONS));
}
function saveButtons(data) {
localStorage.setItem(STORAGE_KEY_BUTTONS, JSON.stringify(data));
localStorage.setItem(STORAGE_KEY_BACKUP, JSON.stringify(data));
}
function loadPosition() {
try {
var saved = localStorage.getItem(STORAGE_KEY_POS);
if (saved) {
var pos = JSON.parse(saved);
if (pos && typeof pos.left === 'number' && typeof pos.top === 'number') return pos;
}
} catch (e) {}
return null;
}
function savePosition(left, top) {
localStorage.setItem(STORAGE_KEY_POS, JSON.stringify({ left: left, top: top }));
}
function isMinimized() {
return localStorage.getItem(STORAGE_KEY_MINIMIZED) === 'true';
}
function setMinimized(state) {
localStorage.setItem(STORAGE_KEY_MINIMIZED, state ? 'true' : 'false');
}
function exportData() {
var data = {
buttons: buttons,
position: loadPosition(),
exported: new Date().toISOString()
};
var blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = 'torn_buttons_backup_' + new Date().toISOString().slice(0,10) + '.json';
a.click();
URL.revokeObjectURL(url);
}
function importData() {
var input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = function(e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function(event) {
try {
var data = JSON.parse(event.target.result);
if (data.buttons && Array.isArray(data.buttons)) {
buttons = data.buttons;
saveButtons(buttons);
if (data.position) {
savePosition(data.position.left, data.position.top);
}
setMinimized(false);
rebuildUI();
alert('Restored ' + buttons.length + ' buttons.');
} else {
alert('Invalid backup file.');
}
} catch (err) {
alert('Error reading file: ' + err.message);
}
};
reader.readAsText(file);
};
input.click();
}
function resetData() {
if (confirm('Reset all buttons to default?')) {
buttons = JSON.parse(JSON.stringify(DEFAULT_BUTTONS));
nextId = buttons.length > 0 ? Math.max(buttons.map(function(b) { return b.id; })) + 1 : 1;
saveButtons(buttons);
localStorage.removeItem(STORAGE_KEY_POS);
setMinimized(false);
rebuildUI();
alert('Buttons reset to default.');
}
}
document.addEventListener('keydown', function(e) {
if (isMinimized()) return;
try {
var target = e.target;
if (target.tagName === 'INPUT' ||
target.tagName === 'TEXTAREA' ||
target.isContentEditable ||
target.closest('[contenteditable="true"], [role="textbox"]')) {
return;
}
var key = e.key.toUpperCase();
var found = buttons.find(function(btn) {
return btn.hotkey && btn.hotkey.toUpperCase() === key;
});
if (found) {
e.preventDefault();
window.location.href = found.url;
}
} catch(err) {}
});
function createFloatingButtons() {
if (document.getElementById('torn-custom-nav')) return;
container = document.createElement('div');
container.id = 'torn-custom-nav';
container.style.cssText =
'position: fixed; z-index: 9999; display: flex; flex-direction: column; gap: 6px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif; cursor: default; user-select: none; ' +
'background: rgba(26, 26, 30, 0.85); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); ' +
'border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; padding: 8px 6px; ' +
'box-shadow: 0 8px 32px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.05); min-width: 140px;';
var pos = loadPosition();
if (pos) {
container.style.left = pos.left + 'px';
container.style.top = pos.top + 'px';
} else {
container.style.bottom = '80px';
container.style.right = '20px';
}
var dragHandle = document.createElement('div');
dragHandle.style.cssText =
'width: 24px; height: 3px; background: rgba(255,255,255,0.15); border-radius: 4px; margin: 0 auto 4px auto; cursor: grab; transition: background 0.2s;';
dragHandle.title = 'Drag to move';
dragHandle.addEventListener('mouseenter', function() {
this.style.background = 'rgba(255,255,255,0.3)';
});
dragHandle.addEventListener('mouseleave', function() {
this.style.background = 'rgba(255,255,255,0.15)';
});
container.appendChild(dragHandle);
renderButtons();
var controlRow = document.createElement('div');
controlRow.style.cssText =
'display: flex; gap: 4px; justify-content: center; margin-top: 4px; padding-top: 6px; border-top: 1px solid rgba(255,255,255,0.06);';
var toggleBtn = document.createElement('button');
toggleBtn.textContent = isMinimized() ? '☰' : '✕';
toggleBtn.style.cssText =
'background: rgba(255,255,255,0.06); color: #888; border: none; border-radius: 6px; cursor: pointer; font-size: 12px; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; transition: all 0.2s;';
toggleBtn.addEventListener('mouseenter', function() {
if (this.textContent === '✕') {
this.style.background = 'rgba(200,50,50,0.2)';
this.style.color = '#ff6b6b';
} else {
this.style.background = 'rgba(50,150,255,0.15)';
this.style.color = '#5b9aff';
}
});
toggleBtn.addEventListener('mouseleave', function() {
this.style.background = 'rgba(255,255,255,0.06)';
this.style.color = '#888';
});
toggleBtn.addEventListener('click', function() {
if (isMinimized()) {
setMinimized(false);
container.querySelectorAll('.nav-btn:not(.no-toggle)').forEach(function(b) {
b.style.display = 'flex';
});
container.querySelectorAll('.nav-btn.no-toggle').forEach(function(b) {
b.style.display = 'flex';
});
exportBtn.style.display = 'flex';
importBtn.style.display = 'flex';
resetBtn.style.display = 'flex';
toggleBtn.textContent = '✕';
} else {
setMinimized(true);
container.querySelectorAll('.nav-btn:not(.no-toggle)').forEach(function(b) {
b.style.display = 'none';
});
container.querySelectorAll('.nav-btn.no-toggle').forEach(function(b) {
b.style.display = 'none';
});
exportBtn.style.display = 'none';
importBtn.style.display = 'none';
resetBtn.style.display = 'none';
toggleBtn.textContent = '☰';
}
});
var exportBtn = document.createElement('button');
exportBtn.textContent = '💾';
exportBtn.title = 'Backup';
exportBtn.style.cssText =
'background: rgba(255,255,255,0.06); color: #888; border: none; border-radius: 6px; cursor: pointer; font-size: 12px; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; transition: all 0.2s;';
exportBtn.addEventListener('mouseenter', function() {
this.style.background = 'rgba(50,150,255,0.15)';
this.style.color = '#5b9aff';
});
exportBtn.addEventListener('mouseleave', function() {
this.style.background = 'rgba(255,255,255,0.06)';
this.style.color = '#888';
});
exportBtn.addEventListener('click', exportData);
var importBtn = document.createElement('button');
importBtn.textContent = '📂';
importBtn.title = 'Restore';
importBtn.style.cssText =
'background: rgba(255,255,255,0.06); color: #888; border: none; border-radius: 6px; cursor: pointer; font-size: 12px; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; transition: all 0.2s;';
importBtn.addEventListener('mouseenter', function() {
this.style.background = 'rgba(50,200,100,0.15)';
this.style.color = '#5bff8a';
});
importBtn.addEventListener('mouseleave', function() {
this.style.background = 'rgba(255,255,255,0.06)';
this.style.color = '#888';
});
importBtn.addEventListener('click', importData);
var resetBtn = document.createElement('button');
resetBtn.textContent = '↺';
resetBtn.title = 'Reset to default';
resetBtn.style.cssText =
'background: rgba(255,255,255,0.06); color: #888; border: none; border-radius: 6px; cursor: pointer; font-size: 12px; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; transition: all 0.2s;';
resetBtn.addEventListener('mouseenter', function() {
this.style.background = 'rgba(200,50,50,0.2)';
this.style.color = '#ff6b6b';
});
resetBtn.addEventListener('mouseleave', function() {
this.style.background = 'rgba(255,255,255,0.06)';
this.style.color = '#888';
});
resetBtn.addEventListener('click', resetData);
controlRow.appendChild(toggleBtn);
controlRow.appendChild(exportBtn);
controlRow.appendChild(importBtn);
controlRow.appendChild(resetBtn);
container.appendChild(controlRow);
if (isMinimized()) {
container.querySelectorAll('.nav-btn:not(.no-toggle)').forEach(function(b) {
b.style.display = 'none';
});
container.querySelectorAll('.nav-btn.no-toggle').forEach(function(b) {
b.style.display = 'none';
});
exportBtn.style.display = 'none';
importBtn.style.display = 'none';
resetBtn.style.display = 'none';
toggleBtn.textContent = '☰';
}
var isDraggingPanel = false;
var startX, startY, origLeft, origTop;
dragHandle.addEventListener('mousedown', function(e) {
isDraggingPanel = true;
var rect = container.getBoundingClientRect();
startX = e.clientX;
startY = e.clientY;
origLeft = rect.left;
origTop = rect.top;
container.style.cursor = 'grabbing';
e.preventDefault();
});
document.addEventListener('mousemove', function(e) {
if (!isDraggingPanel) return;
var dx = e.clientX - startX;
var dy = e.clientY - startY;
var newLeft = origLeft + dx;
var newTop = origTop + dy;
if (newTop < 0) newTop = 0;
container.style.left = newLeft + 'px';
container.style.top = newTop + 'px';
container.style.bottom = 'auto';
container.style.right = 'auto';
});
document.addEventListener('mouseup', function() {
if (isDraggingPanel) {
isDraggingPanel = false;
container.style.cursor = 'default';
var rect = container.getBoundingClientRect();
savePosition(rect.left, rect.top);
}
});
document.body.appendChild(container);
}
function renderButtons() {
var oldBtns = container.querySelectorAll('.nav-btn');
oldBtns.forEach(function(b) { b.remove(); });
for (var i = 0; i < buttons.length; i++) {
var btn = buttons[i];
var wrapper = document.createElement('div');
wrapper.className = 'nav-btn';
wrapper.dataset.btnId = btn.id;
wrapper.style.cssText =
'display: flex; align-items: center; gap: 2px; background: rgba(255,255,255,0.04); border-radius: 8px; transition: all 0.15s; padding: 2px;';
wrapper.addEventListener('mouseenter', function() {
this.style.background = 'rgba(255,255,255,0.08)';
});
wrapper.addEventListener('mouseleave', function() {
this.style.background = 'rgba(255,255,255,0.04)';
});
var dragHandleBtn = document.createElement('span');
dragHandleBtn.textContent = '⠿';
dragHandleBtn.title = 'Drag to reorder';
dragHandleBtn.style.cssText =
'padding: 4px 4px 4px 6px; cursor: grab; color: rgba(255,255,255,0.15); font-size: 12px; user-select: none; transition: color 0.2s; display: flex; align-items: center;';
dragHandleBtn.addEventListener('mouseenter', function() {
this.style.color = 'rgba(255,255,255,0.4)';
});
dragHandleBtn.addEventListener('mouseleave', function() {
this.style.color = 'rgba(255,255,255,0.15)';
});
var navBtn = document.createElement('div');
navBtn.style.cssText =
'color: #ddd; padding: 4px 6px 4px 2px; cursor: pointer; font-size: 13px; font-weight: 500; user-select: none; flex: 1; letter-spacing: 0.2px; display: flex; align-items: center; gap: 6px;';
var badge = document.createElement('span');
badge.textContent = btn.hotkey || '';
badge.style.cssText =
'font-size: 10px; font-weight: 600; color: rgba(255,255,255,0.25); background: rgba(255,255,255,0.06); padding: 1px 6px; border-radius: 4px; margin-left: auto; letter-spacing: 0.5px; font-family: "SF Mono", monospace;';
if (!btn.hotkey) {
badge.style.display = 'none';
}
navBtn.appendChild(document.createTextNode(btn.label));
navBtn.appendChild(badge);
(function(currentBtn) {
navBtn.addEventListener('click', function(e) {
if (isDragging) return;
window.open(currentBtn.url, '_self');
});
})(btn);
var editBtn = document.createElement('span');
editBtn.textContent = '✏️';
editBtn.style.cssText =
'padding: 4px 4px; cursor: pointer; font-size: 11px; opacity: 0.3; border-radius: 4px; transition: all 0.15s; display: flex; align-items: center;';
editBtn.addEventListener('mouseenter', function() {
this.style.opacity = '1';
this.style.background = 'rgba(50,150,255,0.2)';
this.style.color = '#5b9aff';
});
editBtn.addEventListener('mouseleave', function() {
this.style.opacity = '0.3';
this.style.background = 'transparent';
this.style.color = 'inherit';
});
(function(btnId) {
editBtn.addEventListener('click', function(e) {
e.stopPropagation();
openEditor(btnId);
});
})(btn.id);
var delBtn = document.createElement('span');
delBtn.textContent = '🗑️';
delBtn.style.cssText =
'padding: 4px 4px; cursor: pointer; font-size: 11px; opacity: 0.3; border-radius: 4px; transition: all 0.15s; display: flex; align-items: center;';
delBtn.addEventListener('mouseenter', function() {
this.style.opacity = '1';
this.style.background = 'rgba(200,50,50,0.2)';
this.style.color = '#ff6b6b';
});
delBtn.addEventListener('mouseleave', function() {
this.style.opacity = '0.3';
this.style.background = 'transparent';
this.style.color = 'inherit';
});
(function(btnId, btnLabel) {
delBtn.addEventListener('click', function(e) {
e.stopPropagation();
if (confirm('Delete "' + btnLabel + '"?')) {
var newButtons = [];
for (var j = 0; j < buttons.length; j++) {
if (buttons[j].id !== btnId) {
newButtons.push(buttons[j]);
}
}
buttons = newButtons;
if (buttons.length > 0) {
var maxId = 0;
for (var k = 0; k < buttons.length; k++) {
if (buttons[k].id > maxId) maxId = buttons[k].id;
}
nextId = maxId + 1;
} else {
nextId = 1;
}
saveButtons(buttons);
rebuildUI();
}
});
})(btn.id, btn.label);
wrapper.appendChild(dragHandleBtn);
wrapper.appendChild(navBtn);
wrapper.appendChild(editBtn);
wrapper.appendChild(delBtn);
container.insertBefore(wrapper, container.querySelector('.no-toggle'));
(function(wrapper) {
var isDraggingLocal = false;
var ghost = null;
dragHandleBtn.addEventListener('mousedown', function(e) {
isDraggingLocal = true;
isDragging = true;
ghost = wrapper.cloneNode(true);
ghost.style.cssText =
'position: fixed; pointer-events: none; z-index: 99999; opacity: 0.85; background: rgba(30,30,36,0.95); backdrop-filter: blur(8px); border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; padding: 4px 8px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif; color: #ddd; font-size: 13px; font-weight: 500; box-shadow: 0 8px 24px rgba(0,0,0,0.5); min-width: 120px; display: flex; align-items: center; gap: 8px;';
ghost.style.left = (e.clientX - 20) + 'px';
ghost.style.top = (e.clientY - 20) + 'px';
ghost.style.width = wrapper.offsetWidth + 'px';
document.body.appendChild(ghost);
wrapper.style.opacity = '0.3';
e.preventDefault();
});
document.addEventListener('mousemove', function(e) {
if (!isDraggingLocal || !ghost) return;
ghost.style.left = (e.clientX - 20) + 'px';
ghost.style.top = (e.clientY - 20) + 'px';
ghost.style.transform = 'scale(1.02)';
var target = document.elementFromPoint(e.clientX, e.clientY);
if (target) {
var targetWrapper = target.closest('.nav-btn');
if (targetWrapper && targetWrapper !== wrapper && !targetWrapper.classList.contains('no-toggle')) {
var rect = targetWrapper.getBoundingClientRect();
var middle = rect.top + rect.height / 2;
var currentIndex = Array.from(container.children).indexOf(wrapper);
var targetIndex = Array.from(container.children).indexOf(targetWrapper);
if (e.clientY > middle && targetIndex > currentIndex) {
container.insertBefore(wrapper, targetWrapper.nextSibling);
} else if (e.clientY < middle && targetIndex < currentIndex) {
container.insertBefore(wrapper, targetWrapper);
}
}
}
});
document.addEventListener('mouseup', function(e) {
if (!isDraggingLocal) return;
isDraggingLocal = false;
isDragging = false;
if (ghost) {
ghost.remove();
ghost = null;
}
wrapper.style.opacity = '1';
var newOrder = [];
var btns = container.querySelectorAll('.nav-btn');
for (var l = 0; l < btns.length; l++) {
var el = btns[l];
var id = parseInt(el.dataset.btnId);
for (var m = 0; m < buttons.length; m++) {
if (buttons[m].id === id) {
newOrder.push(buttons[m]);
break;
}
}
}
if (newOrder.length > 0 && newOrder.length === buttons.length) {
buttons = newOrder;
saveButtons(buttons);
}
});
})(wrapper);
}
var addWrapper = document.createElement('div');
addWrapper.className = 'nav-btn no-toggle';
addWrapper.style.cssText =
'display: flex; align-items: center; justify-content: center; margin-top: 4px; padding-top: 6px; border-top: 1px solid rgba(255,255,255,0.06);';
var addBtn = document.createElement('div');
addBtn.textContent = '+ Add Button';
addBtn.style.cssText =
'color: rgba(255,255,255,0.35); padding: 4px 14px; font-size: 11px; font-weight: 500; cursor: pointer; user-select: none; border-radius: 6px; transition: all 0.2s; letter-spacing: 0.3px; background: rgba(255,255,255,0.03); border: 1px dashed rgba(255,255,255,0.06);';
addBtn.addEventListener('mouseenter', function() {
this.style.color = 'rgba(255,255,255,0.7)';
this.style.background = 'rgba(255,255,255,0.06)';
this.style.borderColor = 'rgba(255,255,255,0.12)';
});
addBtn.addEventListener('mouseleave', function() {
this.style.color = 'rgba(255,255,255,0.35)';
this.style.background = 'rgba(255,255,255,0.03)';
this.style.borderColor = 'rgba(255,255,255,0.06)';
});
addBtn.addEventListener('click', function() {
openEditor(null);
});
addWrapper.appendChild(addBtn);
container.appendChild(addWrapper);
}
function openEditor(editId) {
var existing = document.getElementById('torn-editor-modal');
if (existing) existing.remove();
var btn = editId ? buttons.find(function(b) { return b.id === editId; }) : null;
var isEdit = !!btn;
var overlay = document.createElement('div');
overlay.id = 'torn-editor-modal';
overlay.style.cssText =
'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.65); backdrop-filter: blur(4px); z-index: 99999; display: flex; align-items: center; justify-content: center;';
var modal = document.createElement('div');
modal.style.cssText =
'background: rgba(22,22,28,0.95); backdrop-filter: blur(16px); padding: 28px 32px; border-radius: 16px; border: 1px solid rgba(255,255,255,0.06); max-width: 420px; width: 90%; box-shadow: 0 24px 64px rgba(0,0,0,0.6);';
var title = document.createElement('h3');
title.textContent = isEdit ? '✏️ Edit Button' : '➕ Add Button';
title.style.cssText = 'color: #eee; margin: 0 0 18px 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif; font-size: 17px; font-weight: 600; letter-spacing: -0.2px;';
var labelInput = document.createElement('input');
labelInput.type = 'text';
labelInput.placeholder = 'Label';
labelInput.value = isEdit ? btn.label : '';
labelInput.style.cssText =
'width: 100%; padding: 10px 12px; margin-bottom: 10px; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.08); border-radius: 8px; color: #eee; font-size: 14px; box-sizing: border-box; outline: none; transition: border-color 0.2s;';
labelInput.addEventListener('focus', function() {
this.style.borderColor = 'rgba(80,180,255,0.3)';
});
labelInput.addEventListener('blur', function() {
this.style.borderColor = 'rgba(255,255,255,0.08)';
});
var urlInput = document.createElement('input');
urlInput.type = 'text';
urlInput.placeholder = 'URL';
urlInput.value = isEdit ? btn.url : '';
urlInput.style.cssText =
'width: 100%; padding: 10px 12px; margin-bottom: 10px; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.08); border-radius: 8px; color: #eee; font-size: 14px; box-sizing: border-box; outline: none; transition: border-color 0.2s;';
urlInput.addEventListener('focus', function() {
this.style.borderColor = 'rgba(80,180,255,0.3)';
});
urlInput.addEventListener('blur', function() {
this.style.borderColor = 'rgba(255,255,255,0.08)';
});
var hotkeyInput = document.createElement('input');
hotkeyInput.type = 'text';
hotkeyInput.placeholder = 'Hotkey (optional)';
hotkeyInput.value = isEdit ? (btn.hotkey || '') : '';
hotkeyInput.style.cssText =
'width: 100%; padding: 10px 12px; margin-bottom: 16px; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.08); border-radius: 8px; color: #eee; font-size: 14px; box-sizing: border-box; outline: none; transition: border-color 0.2s;';
hotkeyInput.addEventListener('focus', function() {
this.style.borderColor = 'rgba(80,180,255,0.3)';
});
hotkeyInput.addEventListener('blur', function() {
this.style.borderColor = 'rgba(255,255,255,0.08)';
});
var btnContainer = document.createElement('div');
btnContainer.style.cssText = 'display: flex; gap: 8px; justify-content: flex-end; margin-top: 4px;';
var cancelBtn = document.createElement('button');
cancelBtn.textContent = 'Cancel';
cancelBtn.style.cssText =
'padding: 8px 18px; background: rgba(255,255,255,0.05); border: none; border-radius: 8px; color: #888; cursor: pointer; font-size: 13px; transition: all 0.2s; font-weight: 500;';
cancelBtn.addEventListener('click', function() { overlay.remove(); });
cancelBtn.addEventListener('mouseenter', function() {
this.style.background = 'rgba(255,255,255,0.08)';
this.style.color = '#aaa';
});
cancelBtn.addEventListener('mouseleave', function() {
this.style.background = 'rgba(255,255,255,0.05)';
this.style.color = '#888';
});
var saveBtn = document.createElement('button');
saveBtn.textContent = isEdit ? 'Save' : 'Add';
saveBtn.style.cssText =
'padding: 8px 22px; background: rgba(80,180,255,0.2); border: none; border-radius: 8px; color: #5b9aff; cursor: pointer; font-size: 13px; transition: all 0.2s; font-weight: 600;';
saveBtn.addEventListener('click', function() {
var label = labelInput.value.trim();
var url = urlInput.value.trim();
var hotkey = hotkeyInput.value.trim().toUpperCase();
if (!label || !url) {
alert('Please fill Label and URL!');
return;
}
if (!url.startsWith('http://') && !url.startsWith('https://')) {
alert('URL must start with http:// or https://');
return;
}
if (hotkey && hotkey.length > 1) {
alert('Hotkey must be a single letter!');
return;
}
if (isEdit) {
var found = buttons.find(function(b) { return b.id === btn.id; });
if (found) {
found.label = label;
found.url = url;
found.hotkey = hotkey || undefined;
}
} else {
buttons.push({ id: nextId++, label: label, url: url, hotkey: hotkey || undefined });
}
saveButtons(buttons);
overlay.remove();
rebuildUI();
});
saveBtn.addEventListener('mouseenter', function() {
this.style.background = 'rgba(80,180,255,0.3)';
});
saveBtn.addEventListener('mouseleave', function() {
this.style.background = 'rgba(80,180,255,0.2)';
});
btnContainer.appendChild(cancelBtn);
btnContainer.appendChild(saveBtn);
modal.appendChild(title);
modal.appendChild(labelInput);
modal.appendChild(urlInput);
modal.appendChild(hotkeyInput);
modal.appendChild(btnContainer);
overlay.appendChild(modal);
document.body.appendChild(overlay);
setTimeout(function() { labelInput.focus(); }, 100);
}
function rebuildUI() {
var old = document.getElementById('torn-custom-nav');
if (old) old.remove();
createFloatingButtons();
}
function init() {
createFloatingButtons();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();