Dominum Macro

A GUI tool containing a macro, click keybinds, themes, joysticks, etc.

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

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.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

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         Dominum Macro
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  A GUI tool containing a macro, click keybinds, themes, joysticks, etc.
// @author       DominumNetwork
// @license      MIT
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const isTop = window === window.top;

    const InputSimulator = {
        getCenter(el) { const rect = el.getBoundingClientRect(); return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }; },
        
        dispatchTouch(action, x, y, pointerId) {
            let el = document.elementFromPoint(x, y);
            
            if (isTop && el && el.tagName === 'IFRAME') {
                let rect = el.getBoundingClientRect();
                let relativeX = x - rect.left;
                let relativeY = y - rect.top;
                try {
                    el.contentWindow.postMessage({ icos: true, type: 'touch', action: action, x: relativeX, y: relativeY, pointerId: pointerId }, '*');
                } catch(e) {}
                return;
            }

            el = el || document.body;
            let type = action === 'down' ? 'pointerdown' : 'pointerup';
            el.dispatchEvent(new PointerEvent(type, { pointerId: pointerId, pointerType: 'touch', clientX: x, clientY: y, bubbles: true, cancelable: true, view: window, isPrimary: pointerId === 1 }));
            let mType = action === 'down' ? 'mousedown' : 'mouseup';
            el.dispatchEvent(new MouseEvent(mType, { clientX: x, clientY: y, bubbles: true, cancelable: true, view: window }));
            if (action === 'up') el.dispatchEvent(new MouseEvent('click', { clientX: x, clientY: y, bubbles: true, cancelable: true }));
        },

        dispatchKey(type, key) {
            let code = 'Key' + key.toUpperCase(); if (key.startsWith('Arrow')) code = key; if (key === 'Space') code = 'Space';
            document.dispatchEvent(new KeyboardEvent(type, { key: key, code: code, keyCode: key.charCodeAt(0) || 0, bubbles: true, cancelable: true, composed: true }));
            
            if (isTop) {
                document.querySelectorAll('iframe').forEach(ifr => {
                    try { ifr.contentWindow.postMessage({ icos: true, type: 'key', action: type, key: key }, '*'); } catch(e){}
                });
            }
        }
    };

    if (!isTop) {
        window.addEventListener('message', (e) => {
            if (!e.data || !e.data.icos) return;
            if (e.data.type === 'touch') {
                InputSimulator.dispatchTouch(e.data.action, e.data.x, e.data.y, e.data.pointerId);
            } else if (e.data.type === 'key') {
                let code = 'Key' + e.data.key.toUpperCase(); if (e.data.key.startsWith('Arrow')) code = e.data.key; if (e.data.key === 'Space') code = 'Space';
                document.dispatchEvent(new KeyboardEvent(e.data.action, { key: e.data.key, code: code, keyCode: e.data.key.charCodeAt(0) || 0, bubbles: true, cancelable: true, composed: true }));
            }
        });
        return;
    }

    const css = `
        :root { --icos-bg: rgba(20, 22, 28, 0.85); --icos-bg-solid: #14161c; --icos-header: rgba(35, 38, 48, 0.95); --icos-border: rgba(255, 255, 255, 0.1); --icos-accent: #00e6b8; --icos-text: #e0e0e0; --icos-text-muted: #888; --icos-danger: #ff4a4a; --icos-success: #4aff80; --icos-radius: 12px; --icos-font: 'Inter', 'Segoe UI', system-ui, sans-serif; --icos-shadow: 0 16px 40px rgba(0, 0, 0, 0.6); --icos-blur: blur(12px); }
        [data-icos-theme="light"] { --icos-bg: rgba(245, 245, 250, 0.9); --icos-bg-solid: #f5f5fa; --icos-header: #ffffff; --icos-border: rgba(0, 0, 0, 0.1); --icos-accent: #0066ff; --icos-text: #1a1a24; --icos-text-muted: #666; }
        [data-icos-theme="cyberpunk"] { --icos-bg: rgba(9, 2, 16, 0.9); --icos-bg-solid: #090210; --icos-header: #fcee0a; --icos-border: #00ff00; --icos-accent: #ff003c; --icos-text: #00ff00; --icos-text-muted: #008800; --icos-danger: #ff0000; }
        [data-icos-theme="synthwave"] { --icos-bg: rgba(36, 17, 54, 0.9); --icos-bg-solid: #241136; --icos-header: #ff00a0; --icos-border: #00e5ff; --icos-accent: #00e5ff; --icos-text: #ffb8ff; }
        [data-icos-theme="hacker"] { --icos-bg: rgba(0, 0, 0, 0.9); --icos-bg-solid: #000000; --icos-header: #001100; --icos-border: #00ff00; --icos-accent: #00ff00; --icos-text: #00ff00; --icos-text-muted: #005500; }
        [data-icos-theme="dracula"] { --icos-bg: rgba(40, 42, 54, 0.9); --icos-bg-solid: #282a36; --icos-header: #44475a; --icos-border: #6272a4; --icos-accent: #ff79c6; --icos-text: #f8f8f2; }
        [data-icos-theme="nord"] { --icos-bg: rgba(46, 52, 64, 0.9); --icos-bg-solid: #2e3440; --icos-header: #3b4252; --icos-border: #4c566a; --icos-accent: #88c0d0; --icos-text: #eceff4; }
        [data-icos-theme="monokai"] { --icos-bg: rgba(39, 40, 34, 0.9); --icos-bg-solid: #272822; --icos-header: #3e3d32; --icos-border: #75715e; --icos-accent: #a6e22e; --icos-text: #f8f8f2; }
        [data-icos-theme="gruvbox"] { --icos-bg: rgba(40, 40, 40, 0.9); --icos-bg-solid: #282828; --icos-header: #3c3836; --icos-border: #504945; --icos-accent: #fabd2f; --icos-text: #ebdbb2; }
        [data-icos-theme="oceanic"] { --icos-bg: rgba(15, 28, 46, 0.9); --icos-bg-solid: #0f1c2e; --icos-header: #1f3a5f; --icos-border: #375a7f; --icos-accent: #00b4d8; --icos-text: #caf0f8; }
        [data-icos-theme="crimson"] { --icos-bg: rgba(20, 0, 0, 0.9); --icos-bg-solid: #140000; --icos-header: #330000; --icos-border: #660000; --icos-accent: #ff0000; --icos-text: #ffcccc; }
        [data-icos-theme="amethyst"] { --icos-bg: rgba(26, 15, 46, 0.9); --icos-bg-solid: #1a0f2e; --icos-header: #2d1a4d; --icos-border: #4a2b80; --icos-accent: #b366ff; --icos-text: #e6ccff; }
        [data-icos-theme="bumblebee"] { --icos-bg: rgba(20, 20, 20, 0.9); --icos-bg-solid: #141414; --icos-header: #ffd700; --icos-border: #cca800; --icos-accent: #ffea00; --icos-text: #ffffff; --icos-text-muted: #aaaaaa; }
        [data-icos-theme="neon_tokyo"] { --icos-bg: rgba(10, 10, 20, 0.9); --icos-bg-solid: #0a0a14; --icos-header: #1a1a3a; --icos-border: #ff00ff; --icos-accent: #00ffff; --icos-text: #ffffff; }
        [data-icos-theme="midnight"] { --icos-bg: rgba(0, 0, 0, 0.95); --icos-bg-solid: #000000; --icos-header: #111111; --icos-border: #333333; --icos-accent: #ffffff; --icos-text: #ffffff; }
        [data-icos-theme="forest"] { --icos-bg: rgba(10, 26, 15, 0.9); --icos-bg-solid: #0a1a0f; --icos-header: #14331d; --icos-border: #265935; --icos-accent: #4ade80; --icos-text: #dcfce7; }
        [data-icos-theme="sunset"] { --icos-bg: rgba(46, 20, 30, 0.9); --icos-bg-solid: #2e141e; --icos-header: #5c273d; --icos-border: #8a3a5c; --icos-accent: #ff7b54; --icos-text: #ffe0d6; }
        @keyframes icos-fade-in { from { opacity: 0; transform: translateY(-10px) scale(0.98); } to { opacity: 1; transform: translateY(0) scale(1); } }
        @keyframes icos-slide-up { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
        @keyframes icos-rgb-border { 0% { background-position: 0% 50%; } 100% { background-position: 200% 50%; } }
        #icos-root { position: fixed; top: 20px; right: 20px; z-index: 9999999; width: 380px; max-height: 85vh; display: flex; flex-direction: column; border-radius: var(--icos-radius); font-family: var(--icos-font); box-shadow: var(--icos-shadow); transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); animation: icos-fade-in 0.4s ease-out; box-sizing: border-box; user-select: none; }
        #icos-root.icos-minimized { height: 46px !important; min-height: 46px !important; overflow: hidden; width: 250px; }
        #icos-root.icos-hidden { opacity: 0; pointer-events: none; transform: scale(0.95); }
        #icos-root.icos-perf * { animation: none !important; transition: none !important; backdrop-filter: none !important; box-shadow: none !important; text-shadow: none !important; }
        #icos-root.icos-perf .icos-rgb-wrapper { display: none !important; }
        #icos-root.icos-perf #icos-inner { background: var(--icos-bg-solid) !important; }
        .icos-rgb-wrapper { position: absolute; top: -2px; left: -2px; right: -2px; bottom: -2px; background: linear-gradient(90deg, #ff0000, #ff7f00, #ffff00, #00ff00, #0000ff, #4b0082, #9400d3, #ff0000); background-size: 200% 100%; border-radius: calc(var(--icos-radius) + 2px); z-index: -1; animation: icos-rgb-border 3s linear infinite; opacity: 0; transition: opacity 0.4s ease; }
        #icos-root.rgb-active .icos-rgb-wrapper { opacity: 1; }
        #icos-inner { background: var(--icos-bg); backdrop-filter: var(--icos-blur); -webkit-backdrop-filter: var(--icos-blur); border-radius: var(--icos-radius); display: flex; flex-direction: column; height: 100%; width: 100%; border: 1px solid var(--icos-border); overflow: hidden; }
        #icos-header { background: var(--icos-header); color: var(--icos-text); padding: 12px 16px; cursor: move; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--icos-border); flex-shrink: 0; height: 46px; box-sizing: border-box; }
        .icos-header-title { font-weight: 700; font-size: 14px; display: flex; align-items: center; gap: 8px; }
        .icos-header-controls { display: flex; gap: 10px; }
        .icos-btn-icon { background: none; border: none; color: var(--icos-text-muted); cursor: pointer; font-size: 16px; padding: 2px; transition: 0.2s; display: flex; align-items: center; justify-content: center; }
        .icos-btn-icon:hover { color: var(--icos-text); transform: scale(1.1); }
        .icos-btn-icon.close:hover { color: var(--icos-danger); }
        #icos-body { padding: 16px; overflow-y: auto; overflow-x: hidden; flex-grow: 1; display: flex; flex-direction: column; gap: 20px; }
        #icos-body::-webkit-scrollbar { width: 6px; }
        #icos-body::-webkit-scrollbar-track { background: transparent; }
        #icos-body::-webkit-scrollbar-thumb { background: var(--icos-border); border-radius: 10px; }
        #icos-body::-webkit-scrollbar-thumb:hover { background: var(--icos-accent); }
        .icos-section { background: rgba(0,0,0,0.2); border: 1px solid var(--icos-border); border-radius: 8px; padding: 14px; position: relative; }
        [data-icos-theme="light"] .icos-section { background: rgba(255,255,255,0.5); }
        .icos-section-title { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--icos-accent); margin: 0 0 12px 0; font-weight: 800; }
        .icos-row { display: flex; gap: 8px; margin-bottom: 8px; align-items: center; }
        .icos-input { background: rgba(0,0,0,0.3); border: 1px solid var(--icos-border); color: var(--icos-text); padding: 8px 12px; border-radius: 6px; font-family: var(--icos-font); font-size: 13px; outline: none; transition: 0.2s; width: 100%; box-sizing: border-box; text-align: center; font-weight: 600; }
        [data-icos-theme="light"] .icos-input { background: #fff; }
        .icos-input:focus { border-color: var(--icos-accent); box-shadow: 0 0 8px rgba(0, 230, 184, 0.3); }
        .icos-select { background: rgba(0,0,0,0.3); border: 1px solid var(--icos-border); color: var(--icos-text); padding: 8px; border-radius: 6px; font-family: var(--icos-font); width: 100%; outline: none; cursor: pointer; }
        .icos-btn { background: var(--icos-header); border: 1px solid var(--icos-border); color: var(--icos-text); padding: 8px 16px; border-radius: 6px; font-family: var(--icos-font); font-size: 13px; font-weight: 600; cursor: pointer; transition: all 0.2s; text-align: center; outline: none; display: flex; align-items: center; justify-content: center; gap: 6px; }
        .icos-btn:hover { background: var(--icos-accent); color: var(--icos-bg-solid); border-color: var(--icos-accent); }
        .icos-btn:active { transform: scale(0.96); }
        .icos-btn-primary { background: rgba(0, 230, 184, 0.1); border-color: var(--icos-accent); color: var(--icos-accent); }
        .icos-btn-primary:hover { background: var(--icos-accent); color: var(--icos-bg-solid); }
        .icos-btn-danger { color: var(--icos-danger); border-color: rgba(255, 74, 74, 0.3); }
        .icos-btn-danger:hover { background: var(--icos-danger); color: #fff; border-color: var(--icos-danger); }
        .icos-marker { position: fixed; z-index: 999998; width: 34px; height: 34px; background: rgba(0, 230, 184, 0.15); border: 2px solid var(--icos-accent); border-radius: 50%; display: flex; justify-content: center; align-items: center; color: var(--icos-text); font-family: var(--icos-font); font-weight: 800; font-size: 14px; text-shadow: 0 2px 4px rgba(0,0,0,0.8); cursor: move; user-select: none; transform: translate(-50%, -50%); backdrop-filter: blur(2px); box-shadow: 0 0 15px rgba(0, 230, 184, 0.3); transition: border-color 0.2s, box-shadow 0.2s; }
        .icos-marker.active { background: var(--icos-accent); color: var(--icos-bg-solid); text-shadow: none; box-shadow: 0 0 20px var(--icos-accent); transform: translate(-50%, -50%) scale(0.9); }
        .icos-marker::after { content: '✛'; position: absolute; color: white; opacity: 0.3; font-size: 12px; pointer-events:none; }
        .icos-joystick-container { position: fixed; z-index: 999997; width: 140px; height: 140px; border-radius: 50%; background: rgba(0, 0, 0, 0.4); border: 2px solid var(--icos-border); backdrop-filter: blur(4px); box-shadow: 0 10px 30px rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; transition: opacity 0.3s; }
        .icos-joystick-header { position: absolute; top: -30px; left: 50%; transform: translateX(-50%); background: var(--icos-header); color: var(--icos-text); font-size: 10px; padding: 4px 10px; border-radius: 12px; border: 1px solid var(--icos-border); cursor: move; white-space: nowrap; font-weight: 700; letter-spacing: 1px; }
        .icos-joystick-knob { width: 48px; height: 48px; border-radius: 50%; background: var(--icos-accent); position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); cursor: pointer; box-shadow: inset 0 -4px 10px rgba(0,0,0,0.3), 0 5px 15px rgba(0, 230, 184, 0.4); transition: transform 0.1s ease-out, background 0.2s; }
        .icos-joystick-knob:active { background: #fff; box-shadow: 0 0 20px #fff; }
        .icos-joy-config { background: rgba(0,0,0,0.1); border-radius: 6px; padding: 10px; margin-bottom: 10px; border: 1px dashed var(--icos-border); }
        #icos-toast-container { position: fixed; bottom: 20px; right: 20px; z-index: 9999999; display: flex; flex-direction: column; gap: 10px; pointer-events: none; }
        .icos-toast { background: var(--icos-header); color: var(--icos-text); padding: 10px 16px; border-radius: 8px; border-left: 4px solid var(--icos-accent); font-family: var(--icos-font); font-size: 13px; box-shadow: var(--icos-shadow); animation: icos-slide-up 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
        .icos-hint { font-size: 11px; color: var(--icos-text-muted); margin-top: -4px; margin-bottom: 8px; display: block; }
        .icos-flex-between { display: flex; justify-content: space-between; align-items: center; }
    `;
    const styleTag = document.createElement('style'); styleTag.innerHTML = css; document.head.appendChild(styleTag);

    const STATE = { theme: 'dark', rgbEnabled: false, minimized: false, hidden: false, perfMode: false, isRecordingMacro: false, macroData: [], macroStart: 0, macroTimers: [], heldKeys: new Set(), joysticksHidden: false };
    const uid = () => Math.random().toString(36).substr(2, 9);
    const Toast = {
        container: null,
        init() { this.container = document.createElement('div'); this.container.id = 'icos-toast-container'; document.body.appendChild(this.container); },
        show(msg, color = 'var(--icos-accent)') {
            if (!this.container) this.init(); const el = document.createElement('div'); el.className = 'icos-toast'; el.style.borderLeftColor = color; el.innerText = msg; this.container.appendChild(el);
            setTimeout(() => { el.style.opacity = '0'; el.style.transform = 'translateY(20px)'; el.style.transition = 'all 0.3s'; setTimeout(() => el.remove(), 300); }, 3000);
        }
    };

    class Draggable {
        constructor(element, handle, isCenterAnchored = false) {
            this.element = element; this.handle = handle || element; this.isCenterAnchored = isCenterAnchored; this.isDragging = false; this.xOffset = 0; this.yOffset = 0;
            this.handle.addEventListener("mousedown", this.dragStart.bind(this)); window.addEventListener("mouseup", this.dragEnd.bind(this)); window.addEventListener("mousemove", this.drag.bind(this));
        }
        dragStart(e) {
            if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON') return;
            const rect = this.element.getBoundingClientRect();
            if (this.isCenterAnchored) { this.xOffset = e.clientX - (rect.left + rect.width / 2); this.yOffset = e.clientY - (rect.top + rect.height / 2); } else { this.xOffset = e.clientX - rect.left; this.yOffset = e.clientY - rect.top; }
            if (e.target === this.handle || this.handle.contains(e.target)) { this.isDragging = true; document.body.style.userSelect = 'none'; }
        }
        dragEnd() { this.isDragging = false; document.body.style.userSelect = ''; }
        drag(e) {
            if (!this.isDragging) return; e.preventDefault(); let newX = e.clientX - this.xOffset, newY = e.clientY - this.yOffset;
            if (this.isCenterAnchored) { this.element.style.left = `${newX}px`; this.element.style.top = `${newY}px`; this.element.style.transform = `translate(-50%, -50%)`; } else { this.element.style.left = `${newX}px`; this.element.style.top = `${newY}px`; this.element.style.right = 'auto'; }
        }
    }

    class BindManager {
        constructor(listElement) { this.listEl = listElement; this.binds = []; this.pointerCounter = 100; }
        addBind() {
            const id = uid(); const pointerId = this.pointerCounter++;
            const marker = document.createElement('div'); marker.className = 'icos-marker'; marker.style.left = `${window.innerWidth / 2}px`; marker.style.top = `${window.innerHeight / 2}px`; marker.innerText = '•';
            document.body.appendChild(marker); new Draggable(marker, marker, true);
            const row = document.createElement('div'); row.className = 'icos-row'; row.id = `bind-row-${id}`;
            row.innerHTML = `<input type="text" class="icos-input" placeholder="Press Key..." maxlength="15" style="width: 70%; text-transform: uppercase;"><button class="icos-btn icos-btn-danger" style="width: 30%;">✖</button>`;
            const input = row.querySelector('input');
            input.addEventListener('keydown', (e) => { e.preventDefault(); const key = e.key === ' ' ? 'Space' : e.key; input.value = key.toUpperCase(); marker.innerText = key.toUpperCase(); const b = this.binds.find(x => x.id === id); if (b) b.key = key; });
            row.querySelector('button').addEventListener('click', () => { row.remove(); marker.remove(); this.binds = this.binds.filter(x => x.id !== id); Toast.show('Bind removed.', 'var(--icos-danger)'); });
            this.listEl.appendChild(row); this.binds.push({ id: id, pointerId: pointerId, key: '', marker: marker }); Toast.show('New target created.');
        }
        hideMarkers() { this.binds.forEach(b => b.marker.style.display = 'none'); }
        showMarkers() { if(STATE.hidden) return; this.binds.forEach(b => b.marker.style.display = 'flex'); }
        handleKeyDown(key) { const bind = this.binds.find(b => b.key.toLowerCase() === key.toLowerCase()); if (bind) { bind.marker.classList.add('active'); this.hideMarkers(); const pos = InputSimulator.getCenter(bind.marker); this.showMarkers(); InputSimulator.dispatchTouch('down', pos.x, pos.y, bind.pointerId); } }
        handleKeyUp(key) { const bind = this.binds.find(b => b.key.toLowerCase() === key.toLowerCase()); if (bind) { bind.marker.classList.remove('active'); this.hideMarkers(); const pos = InputSimulator.getCenter(bind.marker); this.showMarkers(); InputSimulator.dispatchTouch('up', pos.x, pos.y, bind.pointerId); } }
    }

    class JoystickManager {
        constructor(listElement) { this.listEl = listElement; this.joysticks = []; }
        addJoystick() {
            const id = uid(); const wrapper = document.createElement('div'); wrapper.className = 'icos-joystick-container'; wrapper.id = `joy-wrap-${id}`; wrapper.style.left = '100px'; wrapper.style.top = '100px';
            wrapper.innerHTML = `<div class="icos-joystick-header">JOYSTICK</div><div class="icos-joystick-knob"></div>`; document.body.appendChild(wrapper);
            const header = wrapper.querySelector('.icos-joystick-header'), knob = wrapper.querySelector('.icos-joystick-knob'); new Draggable(wrapper, header, false);
            const config = document.createElement('div'); config.className = 'icos-joy-config'; config.id = `joy-conf-${id}`;
            config.innerHTML = `<div class="icos-flex-between" style="margin-bottom:8px;"><span style="font-size:12px; font-weight:bold; color:var(--icos-accent);">Joystick Unit</span><button class="icos-btn icos-btn-icon close">✖</button></div><div class="icos-row"><input type="text" class="icos-input j-n" placeholder="N" value="ArrowUp"><input type="text" class="icos-input j-s" placeholder="S" value="ArrowDown"></div><div class="icos-row"><input type="text" class="icos-input j-w" placeholder="W" value="ArrowLeft"><input type="text" class="icos-input j-e" placeholder="E" value="ArrowRight"></div>`;
            config.querySelector('.close').onclick = () => { wrapper.remove(); config.remove(); this.joysticks = this.joysticks.filter(j => j.id !== id); Toast.show('Joystick destroyed.', 'var(--icos-danger)'); };
            this.listEl.appendChild(config);
            const joyData = { id: id, wrapper: wrapper, knob: knob, configUI: config, binds: { N: 'ArrowUp', S: 'ArrowDown', W: 'ArrowLeft', E: 'ArrowRight' }, activeKeys: new Set(), isDragging: false, center: {x:0, y:0} };
            ['n', 's', 'w', 'e'].forEach(dir => { config.querySelector(`.j-${dir}`).addEventListener('input', (e) => { joyData.binds[dir.toUpperCase()] = e.target.value; }); });
            joyData.updateVisualFromHardware = () => { if (joyData.isDragging) return; const R = wrapper.getBoundingClientRect().width / 2; const MAX_TRAVEL = R * 0.8; let dx = 0, dy = 0; if (STATE.heldKeys.has(joyData.binds.N)) dy -= 1; if (STATE.heldKeys.has(joyData.binds.S)) dy += 1; if (STATE.heldKeys.has(joyData.binds.W)) dx -= 1; if (STATE.heldKeys.has(joyData.binds.E)) dx += 1; if (dx !== 0 && dy !== 0) { const norm = Math.sqrt(0.5); dx *= norm; dy *= norm; } knob.style.transform = `translate(calc(-50% + ${dx * MAX_TRAVEL}px), calc(-50% + ${dy * MAX_TRAVEL}px))`; };
            knob.addEventListener('mousedown', (e) => { joyData.isDragging = true; const rect = wrapper.getBoundingClientRect(); joyData.center = { x: rect.left + rect.width/2, y: rect.top + rect.height/2 }; document.body.style.userSelect = 'none'; e.stopPropagation(); });
            window.addEventListener('mouseup', () => { if (joyData.isDragging) { joyData.isDragging = false; knob.style.transform = `translate(-50%, -50%)`; document.body.style.userSelect = ''; joyData.activeKeys.forEach(k => InputSimulator.dispatchKey('keyup', k)); joyData.activeKeys.clear(); } });
            window.addEventListener('mousemove', (e) => {
                if (!joyData.isDragging) return; const R = wrapper.getBoundingClientRect().width / 2; let dx = e.clientX - joyData.center.x, dy = e.clientY - joyData.center.y, dist = Math.sqrt(dx*dx + dy*dy), angle = Math.atan2(dy, dx);
                if (dist > R) { dx = Math.cos(angle) * R; dy = Math.sin(angle) * R; dist = R; } knob.style.transform = `translate(calc(-50% + ${dx}px), calc(-50% + ${dy}px))`;
                if (dist > R * 0.25) {
                    let deg = angle * (180 / Math.PI); if (deg < 0) deg += 360; let targetKeys = new Set();
                    if (deg >= 337.5 || deg < 22.5) targetKeys.add(joyData.binds.E); else if (deg >= 22.5 && deg < 67.5) { targetKeys.add(joyData.binds.E); targetKeys.add(joyData.binds.S); } else if (deg >= 67.5 && deg < 112.5) targetKeys.add(joyData.binds.S); else if (deg >= 112.5 && deg < 157.5) { targetKeys.add(joyData.binds.S); targetKeys.add(joyData.binds.W); } else if (deg >= 157.5 && deg < 202.5) targetKeys.add(joyData.binds.W); else if (deg >= 202.5 && deg < 247.5) { targetKeys.add(joyData.binds.W); targetKeys.add(joyData.binds.N); } else if (deg >= 247.5 && deg < 292.5) targetKeys.add(joyData.binds.N); else if (deg >= 292.5 && deg < 337.5) { targetKeys.add(joyData.binds.N); targetKeys.add(joyData.binds.E); }
                    joyData.activeKeys.forEach(k => { if (!targetKeys.has(k)) InputSimulator.dispatchKey('keyup', k); }); targetKeys.forEach(k => { if (!joyData.activeKeys.has(k)) InputSimulator.dispatchKey('keydown', k); }); joyData.activeKeys = targetKeys;
                } else { joyData.activeKeys.forEach(k => InputSimulator.dispatchKey('keyup', k)); joyData.activeKeys.clear(); }
            });
            this.joysticks.push(joyData); Toast.show('Joystick generated.');
        }
        toggleVisibility() { STATE.joysticksHidden = !STATE.joysticksHidden; this.joysticks.forEach(j => { j.wrapper.style.display = STATE.joysticksHidden ? 'none' : 'flex'; }); Toast.show(STATE.joysticksHidden ? 'Joysticks hidden' : 'Joysticks revealed'); }
        syncAllHardwareVisuals() { this.joysticks.forEach(j => j.updateVisualFromHardware()); }
    }

    const root = document.createElement('div'); root.id = 'icos-root';
    root.innerHTML = `
        <div class="icos-rgb-wrapper"></div>
        <div id="icos-inner">
            <div id="icos-header"><div class="icos-header-title"><span style="font-size:16px;">⚡</span> Dominum Macro V1</div><div class="icos-header-controls"><button id="icos-btn-min" class="icos-btn-icon" title="Minimize">🗕</button><button id="icos-btn-close" class="icos-btn-icon close" title="Close">✖</button></div></div>
            <div id="icos-body">
                <div class="icos-section"><h3 class="icos-section-title">System Themes</h3><div class="icos-row"><select id="icos-theme-sel" class="icos-select" style="flex: 2;"><option value="dark">Dark Matter</option><option value="light">Light Clean</option><option value="cyberpunk">Cyberpunk 2077</option><option value="synthwave">Synthwave</option><option value="hacker">Hacker (Matrix)</option><option value="dracula">Dracula</option><option value="nord">Nord</option><option value="monokai">Monokai</option><option value="gruvbox">Gruvbox</option><option value="oceanic">Oceanic</option><option value="crimson">Crimson</option><option value="amethyst">Amethyst</option><option value="bumblebee">Bumblebee</option><option value="neon_tokyo">Neon Tokyo</option><option value="midnight">Midnight</option><option value="forest">Forest</option><option value="sunset">Sunset</option></select><button id="icos-perf-btn" class="icos-btn" style="flex: 1; font-size: 11px;">Perf Mode</button><button id="icos-rgb-btn" class="icos-btn" style="flex: 1; font-size: 11px;">RGB Off</button></div><span class="icos-hint" style="margin-top:4px;">Press <b>\`</b> (Backtick) to hide/show this panel in-game.</span></div>
                <div class="icos-section"><h3 class="icos-section-title">Click Keybinds</h3><div id="icos-binds-container" style="margin-bottom: 10px; display:flex; flex-direction:column; gap:5px;"></div><button id="icos-add-bind-btn" class="icos-btn icos-btn-primary" style="width: 100%;">+ Spawn Target Marker</button></div>
                <div class="icos-section"><div class="icos-flex-between" style="margin-bottom:12px;"><h3 class="icos-section-title" style="margin:0;">Joysticks</h3><button id="icos-hide-joy-btn" class="icos-btn" style="padding:4px 8px; font-size:11px;">Toggle Vis</button></div><div id="icos-joysticks-container"></div><button id="icos-add-joy-btn" class="icos-btn icos-btn-primary" style="width: 100%;">+ Spawn Joystick Unit</button></div>
                <div class="icos-section"><h3 class="icos-section-title">Macro (WIP, Might not work)</h3><span class="icos-hint">Records physical keys and physical mouse clicks globally.</span><div class="icos-row"><button id="icos-mac-rec" class="icos-btn" style="flex:1;">⏺ Record</button><button id="icos-mac-play" class="icos-btn" style="flex:1;">▶ Play</button></div><button id="icos-mac-clear" class="icos-btn icos-btn-danger" style="width:100%; margin-top:4px;">⏹ Clear Memory</button><div id="icos-mac-status" style="margin-top:10px; font-size:12px; color:var(--icos-accent); text-align:center; font-family:monospace;">Memory: 0 events</div></div>
            </div>
        </div>
    `;
    document.body.appendChild(root); Toast.init();
    const bindManager = new BindManager(document.getElementById('icos-binds-container')), joyManager = new JoystickManager(document.getElementById('icos-joysticks-container')); new Draggable(root, document.getElementById('icos-header'), false);

    document.getElementById('icos-btn-close').onclick = () => { root.style.display = 'none'; Toast.show('Input Core shutdown.', 'var(--icos-danger)'); };
    document.getElementById('icos-btn-min').onclick = () => { STATE.minimized = !STATE.minimized; STATE.minimized ? (root.classList.add('icos-minimized'), document.getElementById('icos-btn-min').innerText = '🗖') : (root.classList.remove('icos-minimized'), document.getElementById('icos-btn-min').innerText = '🗕'); };
    document.getElementById('icos-theme-sel').onchange = (e) => { STATE.theme = e.target.value; root.setAttribute('data-icos-theme', STATE.theme); Toast.show(`Theme: ${STATE.theme}`); };
    document.getElementById('icos-perf-btn').onclick = () => { STATE.perfMode = !STATE.perfMode; if(STATE.perfMode) { root.classList.add('icos-perf'); document.getElementById('icos-perf-btn').style.color = 'var(--icos-accent)'; Toast.show('Performance Mode ON', 'var(--icos-success)'); } else { root.classList.remove('icos-perf'); document.getElementById('icos-perf-btn').style.color = ''; Toast.show('Performance Mode OFF'); } };
    document.getElementById('icos-rgb-btn').onclick = () => { STATE.rgbEnabled = !STATE.rgbEnabled; STATE.rgbEnabled ? (root.classList.add('rgb-active'), document.getElementById('icos-rgb-btn').innerText = 'RGB On', document.getElementById('icos-rgb-btn').style.color = 'var(--icos-accent)') : (root.classList.remove('rgb-active'), document.getElementById('icos-rgb-btn').innerText = 'RGB Off', document.getElementById('icos-rgb-btn').style.color = ''); };
    document.getElementById('icos-add-bind-btn').onclick = () => bindManager.addBind(); document.getElementById('icos-add-joy-btn').onclick = () => joyManager.addJoystick(); document.getElementById('icos-hide-joy-btn').onclick = () => joyManager.toggleVisibility();

    const recBtn = document.getElementById('icos-mac-rec'), playBtn = document.getElementById('icos-mac-play'), statTxt = document.getElementById('icos-mac-status');
    recBtn.onclick = () => { if (STATE.isRecordingMacro) { STATE.isRecordingMacro = false; recBtn.innerText = '⏺ Record'; recBtn.style.color = ''; recBtn.style.borderColor = ''; statTxt.innerText = `Memory: ${STATE.macroData.length} events loaded`; Toast.show('Macro saved.'); } else { STATE.macroData = []; STATE.macroStart = Date.now(); STATE.isRecordingMacro = true; recBtn.innerText = '⏹ Stop Rec'; recBtn.style.color = 'var(--icos-danger)'; recBtn.style.borderColor = 'var(--icos-danger)'; statTxt.innerText = 'Recording...'; Toast.show('Recording started...', 'var(--icos-danger)'); } };
    playBtn.onclick = () => { if (STATE.macroData.length === 0 || STATE.isRecordingMacro) return; statTxt.innerText = 'Executing Macro...'; STATE.macroTimers.forEach(clearTimeout); STATE.macroTimers = []; STATE.macroData.forEach((action, idx) => { let to = setTimeout(() => { if (action.type === 'keydown' || action.type === 'keyup') InputSimulator.dispatchKey(action.type, action.code); else if (action.type === 'mousedown' || action.type === 'mouseup') InputSimulator.dispatchTouch(action.type === 'mousedown' ? 'down' : 'up', action.x, action.y, 999); if (idx === STATE.macroData.length - 1) { statTxt.innerText = `Memory: ${STATE.macroData.length} events loaded`; Toast.show('Macro finished.'); } }, action.time); STATE.macroTimers.push(to); }); Toast.show('Playing macro...'); };
    document.getElementById('icos-mac-clear').onclick = () => { STATE.macroData = []; STATE.macroTimers.forEach(clearTimeout); statTxt.innerText = 'Memory: 0 events'; Toast.show('Memory cleared.', 'var(--icos-danger)'); };

    window.addEventListener('keydown', (e) => {
        if (e.key === '`' && e.target.tagName !== 'INPUT') { STATE.hidden = !STATE.hidden; STATE.hidden ? (root.classList.add('icos-hidden'), bindManager.binds.forEach(b => b.marker.style.display = 'none')) : (root.classList.remove('icos-hidden'), bindManager.binds.forEach(b => b.marker.style.display = 'flex')); return; }
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
        if (!STATE.heldKeys.has(e.key)) { STATE.heldKeys.add(e.key); bindManager.handleKeyDown(e.key); joyManager.syncAllHardwareVisuals(); }
        if (STATE.isRecordingMacro && !STATE.heldKeys.has('mac_'+e.key)) { STATE.heldKeys.add('mac_'+e.key); STATE.macroData.push({ time: Date.now() - STATE.macroStart, type: 'keydown', code: e.key }); statTxt.innerText = `Recording... [${STATE.macroData.length}]`; }
    });
    window.addEventListener('keyup', (e) => {
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
        STATE.heldKeys.delete(e.key); bindManager.handleKeyUp(e.key); joyManager.syncAllHardwareVisuals();
        if (STATE.isRecordingMacro) { STATE.heldKeys.delete('mac_'+e.key); STATE.macroData.push({ time: Date.now() - STATE.macroStart, type: 'keyup', code: e.key }); statTxt.innerText = `Recording... [${STATE.macroData.length}]`; }
    });
    window.addEventListener('mousedown', (e) => { if (e.target.closest('#icos-root') || e.target.closest('.icos-joystick-container') || e.target.closest('.icos-marker')) return; if (STATE.isRecordingMacro) STATE.macroData.push({ time: Date.now() - STATE.macroStart, type: 'mousedown', x: e.clientX, y: e.clientY }); });
    window.addEventListener('mouseup', (e) => { if (e.target.closest('#icos-root') || e.target.closest('.icos-joystick-container') || e.target.closest('.icos-marker')) return; if (STATE.isRecordingMacro) STATE.macroData.push({ time: Date.now() - STATE.macroStart, type: 'mouseup', x: e.clientX, y: e.clientY }); });

    setTimeout(() => { Toast.show('Dominum Macro Initialized. Press ` to hide UI.', 'var(--icos-success)'); }, 500);
})();