Greasy Fork is available in English.

Torn Custom Navigation Buttons

Floating nav buttons with drag, edit, hotkeys, backup, and persistent minimize.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

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.

(I already have a user style manager, let me install it!)

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

})();