Click Helper [bloxd io/bloxd/bloxd.io]

Auto Clicker (L/R), Custom CPS, Account Generator, Killaura, Aimbot, BHOP, ViewModel, Player ESP.

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         Click Helper [bloxd io/bloxd/bloxd.io]
// @namespace    https://bloxd.io
// @icon         https://i.ibb.co/rfmXMwQz/Click-Helper-Icon.png

// @version      1.5.1.8
// @description  Auto Clicker (L/R), Custom CPS, Account Generator, Killaura, Aimbot, BHOP, ViewModel, Player ESP.
// @author       MakeItOrBreakIt
// @license      MIT
// @match        *://*.bloxd.io/*

// @match        *://*.bloxd.com/*
// @match        *://*.bloxd.dev/*
// @match        *://*.bloxdhop.io/*
// @match        *://*.bloxdunblocked.*/*
// @match        *://*.unbloxd.*/*
// @match        *://*.playbloxd.com/*

// @match        *://*.buildhub.*/*
// @match        *://*.skillhub.vip/*
// @match        *://*.classcraft.*/*
// @match        *://*.collabspace.space/*
// @match        *://*.creativebuilding.*/*
// @match        *://*.bedwars*.net/*
// @match        *://*.bedwars*.space/*
// @match        *://*.buildminecreate.com/*
// @match        *://*.iogamesunblocked.com/*
// @match        *://*.unblockedgames.club/*

// @grant        none
// @run-at       document-start
// ==/UserScript==
/*
 * MIT License
 * Copyright (c) 2026 MakeItOrBreakIt
 */
(() => {
  'use strict';
  const attempt = (fn, fb = null) => { try { return fn(); } catch (_) { return fb; } };
  const vals = o => Object.values(o ?? {});
  const ks = o => Object.keys(o ?? {});

  const _call = Function.prototype.call;

  const bloxd = {
  noa: null,
  init() {
    let captured = false;

    Object.defineProperty(Function.prototype, 'call', {
      enumerable: false,
      configurable: true,
      writable: true,
      value: function (thisArg, ...args) {
        if (!captured) {
          const c = args[0];
          if (c && c.entities && c.bloxd) {
            captured = true;
            window.noa = c;
            bloxd.noa = c;
            console.log(
              "%c[Click Helper]%c Injected successfully!",
              "background: linear-gradient(90deg, #7c5cfc, #c84cff); color: #fff; font-weight: bold; padding: 3px 8px; border-radius: 4px;",
              "color: #50FA7B; font-weight: bold; margin-left: 6px;"
            );
            Function.prototype.call = _call;
          }
        }
        return _call.apply(this, [thisArg, ...args]);
        }
      });
    }
  };

  const _attackFnCache = new Map();

  const game = {
    _held: null,
    inGame() { return !!(bloxd.noa?.bloxd?.client?.msgHandler); },
    getPos(id) { return attempt(() => bloxd.noa.entities.getState(id, 'position').position); },
    getIds() { return attempt(() => bloxd.noa?.bloxd?.getPlayerIds?.() ?? {}, {}); },
    getMovement(id = 1) { return attempt(() => bloxd.noa.entities.getState(id, 'movement')); },
    getHeld(id = 1) {
      if (!bloxd.noa) return null;
      if (!this._held) this._held = vals(bloxd.noa.entities).find(fn => {
        if (typeof fn !== 'function' || fn.length !== 1) return false;
        const s = fn.toString();
        return s.length < 80 && s.includes(').') && !s.includes('opWrapper');
      });
      return attempt(() => this._held?.(id));
    },
    _findAttackFn(item) {
      const proto = Object.getPrototypeOf(item);
      if (_attackFnCache.has(proto)) return _attackFnCache.get(proto);
      const fn = Object.getOwnPropertyNames(proto)
        .map(k => item[k])
        .find(f => typeof f === 'function' && f.length === 3)
        ?? null;
      if (fn) _attackFnCache.set(proto, fn);
      return fn;
    },
    doAttack(id) {
      attempt(() => {
        const item = attempt(() => game.getHeld(1)?.heldItemState?.fistItem);
        if (!item) return;
        const fn = game._findAttackFn(item);
        if (!fn) return;
        let offset = [0, 0, 0];
        const jitter = state.killaura.jitter;
        if (jitter > 0) {
          const cam = bloxd.noa?.camera;
          if (cam && cam.heading !== undefined && cam.pitch !== undefined) {
            const yaw = cam.heading + (Math.random() - 0.5) * jitter * 0.35;
            const pitch = cam.pitch + (Math.random() * 0.8 - 0.2) * jitter * 0.5;
            const amt = jitter * 0.12;
            offset = [
              Math.sin(yaw) * Math.cos(pitch) * amt,
              Math.sin(pitch) * amt,
              Math.cos(yaw) * Math.cos(pitch) * amt
            ];
          } else {
            offset = [
              (Math.random() - 0.5) * jitter,
              (Math.random() - 0.5) * jitter * 0.8,
              (Math.random() - 0.5) * jitter * 0.6
            ];
          }
        }
        fn.call(item, offset, id.toString(), 'HeadMesh');
      });
    },
    fireInput(action, down) {
      attempt(() => {
        const inp = bloxd.noa?.inputs;
        if (!inp) return;
        (down ? inp.down : inp.up)._events?.[action]?.(0);
      });
    },
    updateESP(enabled, now) {
      if (!this._espCache) this._espCache = { lastUpdate: 0, interval: 100 };
      if (now - this._espCache.lastUpdate < this._espCache.interval) return;
      this._espCache.lastUpdate = now;
      
      attempt(() => {
        if (!bloxd.noa) return;
        const r = vals(bloxd.noa)[12];
        if (!r) return;
        const tm = vals(r).find(v => v?.thinMeshes)?.thinMeshes;
        if (Array.isArray(tm)) {
          tm.forEach(i => {
            const m = i?.meshVariations?.__DEFAULT__?.mesh;
            if (m && typeof m.renderingGroupId === "number") {
              m.renderingGroupId = enabled ? 2 : 0;
            }
          });
        }
      });
    }
  };

  const state = {
    killaura: { enabled: false, delay: 50, range: 6.7, jitter: 0.35, _last: 0 },
    aimbot: { enabled: false, range: 16, smooth: 0.8, delay: 8, fov: 135, _last: 0 },
    clicker: { left: { enabled: false, iv: null }, right: { enabled: false, iv: null }, cpsMin: 14, cpsMax: 19 },
    movement: { bhop: false, bhopChance: 100 },
    visuals: { esp: false },
    viewmodel: { enabled: false, pos: { x: 0, y: 0, z: 0 }, rot: { x: 0, y: 0, z: 0 }, spin: false, spinSpeed: { x: 0, y: 2, z: 0 }, spinAngle: { x: 0, y: 0, z: 0 }, _lastTime: 0 },
    binds: { ka: 'KeyK', aim: '', lc: 'KeyR', rc: 'KeyF', bhop: 'KeyZ', esp: '', vm: '', menu: 'ShiftRight' }
  };

  let savedBinds = {};
  const bindsStr = localStorage.getItem('clickHelper-keybinds');
  if (bindsStr) { try { savedBinds = JSON.parse(bindsStr); } catch (_) {} }
  Object.assign(state.binds, savedBinds);

  let savedSettings = {};
  const settingsStr = localStorage.getItem('clickHelper-settings');
  if (settingsStr) { try { savedSettings = JSON.parse(settingsStr); } catch (_) {} }
  if (savedSettings.killaura) Object.assign(state.killaura, savedSettings.killaura);
  if (savedSettings.aimbot) Object.assign(state.aimbot, savedSettings.aimbot);
  if (savedSettings.clicker) {
    state.clicker.cpsMin = savedSettings.clicker.cpsMin ?? state.clicker.cpsMin;
    state.clicker.cpsMax = savedSettings.clicker.cpsMax ?? state.clicker.cpsMax;
  }
  if (savedSettings.movement) state.movement.bhopChance = savedSettings.movement.bhopChance ?? state.movement.bhopChance;
  if (savedSettings.viewmodel) {
    if (savedSettings.viewmodel.pos) Object.assign(state.viewmodel.pos, savedSettings.viewmodel.pos);
    if (savedSettings.viewmodel.rot) Object.assign(state.viewmodel.rot, savedSettings.viewmodel.rot);
    if (savedSettings.viewmodel.spinSpeed) Object.assign(state.viewmodel.spinSpeed, savedSettings.viewmodel.spinSpeed);
  }

  function saveBinds() { localStorage.setItem('clickHelper-keybinds', JSON.stringify(state.binds)); }
  function saveSettings() {
    const settings = {
      killaura: { delay: state.killaura.delay, range: state.killaura.range, jitter: state.killaura.jitter },
      aimbot: { range: state.aimbot.range, smooth: state.aimbot.smooth, delay: state.aimbot.delay },
      clicker: { cpsMin: state.clicker.cpsMin, cpsMax: state.clicker.cpsMax },
      movement: { bhopChance: state.movement.bhopChance },
      viewmodel: { pos: state.viewmodel.pos, rot: state.viewmodel.rot, spinSpeed: state.viewmodel.spinSpeed }
    };
    localStorage.setItem('clickHelper-settings', JSON.stringify(settings));
  }
  let saveTimeout;
  function debouncedSave() {
    clearTimeout(saveTimeout);
    saveTimeout = setTimeout(() => { saveBinds(); saveSettings(); }, 300);
  }

  function toggleClicker(side) {
    const s = state.clicker[side];
    const action = side === 'left' ? 'primary-fire' : 'alt-fire';
    s.enabled = !s.enabled;
    clearTimeout(s.iv);
    if (s.enabled) {
      const tick = () => {
        if (!s.enabled) return;
        game.fireInput(action, true);
        setTimeout(() => game.fireInput(action, false), 20);
        let next = 1000 / (state.clicker.cpsMin + Math.random() * (state.clicker.cpsMax - state.clicker.cpsMin));
        s.iv = setTimeout(tick, Math.max(10, next));
      };
      tick();
    }
    return s.enabled;
  }

  function clearAndReload() {
    document.cookie.split(';').forEach(c => {
      const n = c.split('=')[0].trim();
      [`path=/`, `path=/;domain=${location.hostname}`, `path=/;domain=.${location.hostname}`]
        .forEach(p => document.cookie = `${n}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;${p}`);
    });
    localStorage.clear();
    sessionStorage.clear();
    location.reload();
  }

  function bindDisplay(code) {
    if (!code) return '—';
    return code.replace('Key', '').replace('Digit', '').replace('ShiftRight', 'RS').replace('ShiftLeft', 'LS')
      .replace('ControlLeft', 'LC').replace('ControlRight', 'RC').replace('AltLeft', 'LA').replace('AltRight', 'RA')
      .replace('Backquote', '`').replace('Backslash', '\\').replace('BracketLeft', '[').replace('BracketRight', ']')
      .replace('Semicolon', ';').replace('Quote', "'").replace('Comma', ',').replace('Period', '.')
      .replace('Slash', '/').replace('Minus', '-').replace('Equal', '=').replace('Space', 'SPC')
      .replace('Tab', 'TAB').replace('CapsLock', 'CAPS').replace('Enter', 'ENT').replace('Escape', 'ESC').replace('Arrow', '');
  }

  function safeAppend(el) {
    if (document.body) return document.body.appendChild(el);
    const observer = new MutationObserver(() => {
      if (document.body) { observer.disconnect(); document.body.appendChild(el); }
    });
    observer.observe(document.documentElement, { childList: true });
  }

  function buildUI() {
    if (document.getElementById('ch-root')) return;

    const css = document.createElement('style');
    css.textContent = `
      @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
      @import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css');
      #ch-root, #ch-root * { box-sizing: border-box; }
      :root {
        --ghost-bg: rgba(18, 18, 24, 0.95);
        --ghost-panel: rgba(28, 28, 40, 0.85);
        --ghost-panel-border: rgba(255,255,255,0.06);
        --ghost-accent-1: #7c5cfc;
        --ghost-accent-2: #c84cff;
        --ghost-text: #e8e4f0;
        --ghost-text-dim: #8a8499;
        --ghost-on: #7c5cfc;
        --ghost-off: rgba(60,55,80,0.6);
        --ghost-danger: #ff4466;
        --ghost-success: #44ffaa;
      }
      #ch-root { position: fixed; top: 20px; left: 20px; width: 300px; background: var(--ghost-bg); border: 1px solid var(--ghost-panel-border); border-radius: 10px; font-family: 'Inter', sans-serif; font-size: 11px; color: var(--ghost-text); z-index: 99999; box-shadow: 0 16px 40px rgba(0,0,0,0.6), 0 0 1px rgba(124,92,252,0.4); user-select: none; transition: opacity 0.3s ease, transform 0.3s ease; backdrop-filter: blur(16px) saturate(1.2); transform-origin: center center; }
      #ch-root.hidden { opacity: 0; pointer-events: none; transform: scale(0.92); }
      #ch-root.mini #ch-body, #ch-root.mini #ch-tabs { display: none; }
      #ch-hdr { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: linear-gradient(-45deg, #7c5cfc, #c84cff, #5c9cff, #7c5cfc); background-size: 300% 300%; animation: ghostGradient 4s ease infinite; cursor: grab; border-radius: 9px 9px 0 0; }
      @keyframes ghostGradient { 0%{background-position:0% 50%} 50%{background-position:100% 50%} 100%{background-position:0% 50%} }
      #ch-title { font-weight: 700; font-size: 14px; color: #fff; text-shadow: 0 1px 4px rgba(0,0,0,0.3); letter-spacing: 0.5px; }
      #ch-title span { opacity: 0.7; font-weight: 500; font-size: 10px; margin-left: 6px; }
      .ch-hbtn { background: rgba(255,255,255,0.2); border: none; color: #fff; font-size: 12px; cursor: pointer; padding: 2px 8px; border-radius: 4px; transition: 0.2s; display: flex; align-items: center; justify-content: center; width: 28px; height: 24px; }
      .ch-hbtn:hover { background: rgba(255,255,255,0.4); }
      .ch-hbtn i { font-size: 13px; }

      /* === TABS === */
      #ch-tabs { display: flex; background: rgba(0,0,0,0.25); border-bottom: 1px solid var(--ghost-panel-border); padding: 0; margin: 0; gap: 0; }
      .ch-tab { flex: 1; padding: 10px 0 9px 0; text-align: center; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; color: var(--ghost-text-dim); cursor: pointer; transition: color 0.2s, background 0.2s, box-shadow 0.2s; position: relative; border: none; background: transparent; outline: none; font-family: 'Inter', sans-serif; }
      .ch-tab:hover { color: var(--ghost-text); background: rgba(124,92,252,0.06); }
      .ch-tab.active { color: #fff; }
      .ch-tab.active::after { content: ''; position: absolute; bottom: 0; left: 15%; right: 15%; height: 2.5px; background: linear-gradient(90deg, var(--ghost-accent-1), var(--ghost-accent-2)); border-radius: 2px 2px 0 0; box-shadow: 0 0 8px rgba(124,92,252,0.5); }
      .ch-tab i { margin-right: 5px; font-size: 12px; }

      /* === TAB PANELS === */
      .ch-tab-panel { display: none; flex-direction: column; gap: 8px; }
      .ch-tab-panel.active { display: flex; }

      #ch-body { padding: 10px; display: flex; flex-direction: column; gap: 8px; max-height: calc(100vh - 120px); overflow-y: auto; }
      #ch-body::-webkit-scrollbar { width: 4px; }
      #ch-body::-webkit-scrollbar-thumb { background: var(--ghost-accent-1); border-radius: 4px; }
      .ch-sec { display: flex; flex-direction: column; gap: 6px; background: var(--ghost-panel); padding: 10px 12px; border-radius: 6px; border: 1px solid var(--ghost-panel-border); }
      .ch-lbl { font-size: 10px; font-weight: 700; color: var(--ghost-text-dim); text-transform: uppercase; letter-spacing: 1px; padding-bottom: 4px; border-bottom: 1px solid rgba(255,255,255,0.04); margin-bottom: 2px; }
      .mod-row { display: flex; align-items: center; justify-content: space-between; padding: 2px 0; }
      .mod-left { display: flex; align-items: center; gap: 8px; flex: 1; }
      .mod-name { font-size: 12px; font-weight: 600; color: var(--ghost-text); }
      .tg { width: 30px; height: 16px; background: var(--ghost-off); border-radius: 8px; position: relative; cursor: pointer; transition: 0.25s; flex-shrink: 0; }
      .tg::after { content: ''; width: 12px; height: 12px; background: #888; border-radius: 50%; position: absolute; top: 2px; left: 2px; transition: 0.25s; }
      .tg.on { background: var(--ghost-on); box-shadow: 0 0 10px rgba(124,92,252,0.4); }
      .tg.on::after { left: 16px; background: #fff; }
      .kb { background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.08); color: var(--ghost-text-dim); font-size: 9px; font-weight: 600; padding: 2px 6px; border-radius: 4px; cursor: pointer; transition: 0.2s; display: flex; align-items: center; gap: 4px; font-family: monospace; }
      .kb:hover { background: rgba(124,92,252,0.15); border-color: rgba(124,92,252,0.4); color: var(--ghost-text); }
      .kb.listening { border-color: var(--ghost-accent-2); color: var(--ghost-accent-2); animation: kbpulse 0.8s ease infinite; }
      @keyframes kbpulse { 0%,100%{opacity:1;} 50%{opacity:0.4;} }
      .kb-x { font-size: 8px; color: rgba(255,100,100,0.5); cursor: pointer; }
      .kb-x:hover { color: var(--ghost-danger); }
      .val-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
      .val-item { display: flex; flex-direction: column; gap: 4px; }
      .val-label { font-size: 9px; color: var(--ghost-text-dim); font-weight: 600; text-transform: uppercase; }
      .val-inp { background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.08); border-radius: 4px; color: var(--ghost-text); font-family: monospace; font-size: 11px; padding: 4px 6px; text-align: center; outline: none; transition: 0.2s; width: 100%; }
      .val-inp:focus { border-color: var(--ghost-accent-1); box-shadow: 0 0 8px rgba(124,92,252,0.2); }
      .sl-row { display: flex; align-items: center; gap: 8px; margin-top: 6px; width: 100%; }
      .sl-label { font-size: 10px; color: var(--ghost-text-dim); font-weight: 600; width: 36px; flex-shrink: 0; }
      input[type=range] { flex: 1; -webkit-appearance: none; background: transparent; cursor: pointer; }
      input[type=range]::-webkit-slider-runnable-track { height: 4px; background: rgba(255,255,255,0.08); border-radius: 2px; }
      input[type=range]::-webkit-slider-thumb { height: 12px; width: 12px; border-radius: 50%; background: #fff; cursor: pointer; -webkit-appearance: none; margin-top: -4px; box-shadow: 0 0 6px rgba(124,92,252,0.6); }
      .sl-val-inp { width: 44px; flex-shrink: 0; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.1); color: var(--ghost-accent-1); font-family: monospace; font-size: 10px; font-weight: 600; text-align: center; border-radius: 4px; outline: none; padding: 3px 0; }
      .sl-val-inp:focus { border-color: var(--ghost-accent-1); }
      .ch-action-btn { width: 100%; padding: 8px; border-radius: 6px; border: 1px solid rgba(255,68,102,0.3); background: rgba(255,68,102,0.1); color: #ff6688; font-weight: 700; font-size: 11px; cursor: pointer; transition: 0.2s; text-transform: uppercase; letter-spacing: 0.5px; }
      .ch-action-btn:hover { background: rgba(255,68,102,0.2); border-color: #ff4466; color: #ff8899; }
      /* Text input fields */
      .ch-text-inp { background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.08); border-radius: 4px; color: var(--ghost-text); font-family: 'Inter', sans-serif; font-size: 11px; padding: 6px 8px; outline: none; transition: 0.2s; width: 100%; }
      .ch-text-inp:focus { border-color: var(--ghost-accent-1); box-shadow: 0 0 8px rgba(124,92,252,0.2); }
      .ch-text-inp::placeholder { color: var(--ghost-text-dim); opacity: 0.6; }
      #ch-status { padding: 8px 16px; background: rgba(0,0,0,0.3); border-top: 1px solid var(--ghost-panel-border); font-size: 9px; color: var(--ghost-text-dim); display: flex; justify-content: space-between; align-items: center; border-radius: 0 0 9px 9px; font-weight: 600; letter-spacing: 0.5px; }
      #ch-status .dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; margin-right: 6px; }
      #ch-status .dot.green { background: var(--ghost-success); box-shadow: 0 0 8px rgba(68,255,170,0.5); }
      #ch-status .dot.red { background: var(--ghost-danger); box-shadow: 0 0 8px rgba(255,68,102,0.5); }
	  #ch-gen { background: rgba(255,68,102,0.3) !important; color: #ff8899 !important; }
	  #ch-gen:hover { background: rgba(255,68,102,0.5) !important; }
	  #ch-root input[type="text"],
	  #ch-root input[type="number"],
	  #ch-root textarea {
		   background: rgba(0,0,0,0.3) !important;
		   border: 1px solid rgba(255,255,255,0.08) !important;
		   border-radius: 4px !important;
		   color: var(--ghost-text) !important;
		   font-family: 'Inter', monospace !important;
		   font-size: 11px !important;
		   padding: 4px 6px !important;
		   outline: none !important;
		   transition: 0.2s !important;
		   box-shadow: none !important;
	  }

	  #ch-root input[type="text"]:focus,
	  #ch-root input[type="number"]:focus,
	  #ch-root textarea:focus {
		   border-color: var(--ghost-accent-1) !important;
		   box-shadow: 0 0 8px rgba(124,92,252,0.2) !important;
	  }

	  #ch-root input::placeholder,
	  #ch-root textarea::placeholder {
		   color: var(--ghost-text-dim) !important;
		   opacity: 0.6 !important;
	  }
    `;
    document.head.appendChild(css);

    const mkToggle = (id, name, bindKey) => {
      const disp = bindDisplay(state.binds[bindKey]);
      const hasKey = !!state.binds[bindKey];
      return `
        <div class="mod-row" id="row-${id}">
          <div class="mod-left">
            <div class="tg" id="tg-${id}"></div>
            <span class="mod-name">${name}</span>
          </div>
          ${bindKey ? `
          <div class="mod-right">
            <div class="kb" id="bind-${bindKey}" data-action="${bindKey}">
              <span class="kb-text">${hasKey ? disp : '—'}</span>
              ${hasKey ? `<span class="kb-x" data-unbind="${bindKey}">✕</span>` : `<span class="kb-x" data-unbind="${bindKey}" style="display:none">✕</span>`}
            </div>
          </div>` : ''}
        </div>`;
    };

    const mkSlider = (id, label, min, max, val, step = '1') =>
      `<div class="sl-row">
        <span class="sl-label">${label}</span>
        <input type="range" id="${id}" min="${min}" max="${max}" value="${val}" step="${step}">
        <input type="number" class="sl-val-inp" id="${id}-v" value="${val}" step="${step}" min="${min}" max="${max}">
      </div>`;

    const mkTextInput = (id, placeholder, value = '') =>
      `<input type="text" class="ch-text-inp" id="${id}" placeholder="${placeholder}" value="${value}">`;

    const root = document.createElement('div');
    root.id = 'ch-root';
    root.innerHTML = `
      <div id="ch-hdr">
        <span id="ch-title">CLICK HELPER <span>v1.5.1.8</span></span>
        <div style="display:flex;gap:5px;">
          <button class="ch-hbtn" id="ch-gen" title="New Account" style="background: rgba(255,68,102,0.3); color: #ff8899;"><i class="fa-solid fa-user"></i></button>
          <button class="ch-hbtn" id="ch-inject" title="Re-Inject"><i class="fa-solid fa-syringe"></i></button>
          <button class="ch-hbtn" id="ch-min" title="Minimize">—</button>
        </div>
      </div>
      <div id="ch-tabs">
        <button class="ch-tab active" data-tab="combat"><i class="fa-solid fa-crosshairs"></i>Combat</button>
        <button class="ch-tab" data-tab="movement"><i class="fa-solid fa-person-running"></i>Move</button>
        <button class="ch-tab" data-tab="visuals"><i class="fa-solid fa-eye"></i>Visuals</button>
      </div>
      <div id="ch-body">
        <!-- COMBAT TAB -->
        <div class="ch-tab-panel active" data-panel="combat">
          <div class="ch-sec">
            <div class="ch-lbl">Kill Aura</div>
            ${mkToggle('ka', 'Kill Aura', 'ka')}
            <div style="margin-top: 6px;">
              ${mkSlider('ka-delay', 'Delay', 0, 1000, state.killaura.delay, 5)}
              ${mkSlider('ka-range', 'Range', 1, 20, state.killaura.range, 0.1)}
              ${mkSlider('ka-jitter', 'Jitter', 0, 0.6, state.killaura.jitter, 0.01)}
            </div>
          </div>
          <div class="ch-sec">
            <div class="ch-lbl">Aimbot</div>
            ${mkToggle('aim', 'Aimbot', 'aim')}
            <div style="margin-top: 6px;">
              ${mkSlider('aim-range', 'Range', 5, 100, state.aimbot.range, 1)}
              ${mkSlider('aim-smooth', 'Smooth', 0.01, 1, state.aimbot.smooth, 0.01)}
              ${mkSlider('aim-delay', 'Delay', 0, 500, state.aimbot.delay, 5)}
              ${mkSlider('aim-fov', 'FOV', 1, 180, state.aimbot.fov, 1)}
            </div>
          </div>
          <div class="ch-sec">
            <div class="ch-lbl">Auto Clicker</div>
            ${mkToggle('lc', 'Left Click', 'lc')}
            ${mkToggle('rc', 'Right Click', 'rc')}
            <div class="val-grid" style="margin-top: 6px;">
              <div class="val-item"><span class="val-label">Min CPS</span><input class="val-inp" id="inp-cmin" type="number" value="${state.clicker.cpsMin}" step="1" min="1" max="50"></div>
              <div class="val-item"><span class="val-label">Max CPS</span><input class="val-inp" id="inp-cmax" type="number" value="${state.clicker.cpsMax}" step="1" min="1" max="50"></div>
            </div>
          </div>
        </div>

        <!-- MOVEMENT TAB -->
        <div class="ch-tab-panel" data-panel="movement">
          <div class="ch-sec">
            <div class="ch-lbl">Movement</div>
            ${mkToggle('bhop', 'BHOP', 'bhop')}
            <div style="margin-top: 4px;">
              ${mkSlider('bhop-chance', '%', 1, 100, state.movement.bhopChance)}
            </div>
          </div>
        </div>

        <!-- VISUALS TAB -->
        <div class="ch-tab-panel" data-panel="visuals">
          <div class="ch-sec">
            <div class="ch-lbl">ESP</div>
            ${mkToggle('esp', 'Player ESP', 'esp')}
          </div>
          <div class="ch-sec">
            <div class="ch-lbl">ViewModel</div>
            ${mkToggle('vm', 'ViewModel', 'vm')}
            <div style="font-size: 8px; font-weight: 700; color: var(--ghost-text-dim); margin-top: 8px;">POSITION</div>
            ${mkSlider('vm-px', 'X', -3, 3, state.viewmodel.pos.x, '0.05')}
            ${mkSlider('vm-py', 'Y', -3, 3, state.viewmodel.pos.y, '0.05')}
            ${mkSlider('vm-pz', 'Z', -3, 3, state.viewmodel.pos.z, '0.05')}
            <div style="font-size: 8px; font-weight: 700; color: var(--ghost-text-dim); margin-top: 10px;">ROTATION</div>
            ${mkSlider('vm-rx', 'X', -3.14, 3.14, state.viewmodel.rot.x, '0.01')}
            ${mkSlider('vm-ry', 'Y', -3.14, 3.14, state.viewmodel.rot.y, '0.01')}
            ${mkSlider('vm-rz', 'Z', -3.14, 3.14, state.viewmodel.rot.z, '0.01')}
            <div style="border-top: 1px solid rgba(255,255,255,0.04); margin: 8px 0 4px 0;"></div>
            ${mkToggle('spin', 'Spin Tool', '')}
            <div style="font-size: 8px; font-weight: 700; color: var(--ghost-text-dim); margin-top: 6px;">SPIN SPEED</div>
            ${mkSlider('spin-sx', 'X', -10, 10, state.viewmodel.spinSpeed.x, '0.1')}
            ${mkSlider('spin-sy', 'Y', -10, 10, state.viewmodel.spinSpeed.y, '0.1')}
            ${mkSlider('spin-sz', 'Z', -10, 10, state.viewmodel.spinSpeed.z, '0.1')}
          </div>
        </div>
      </div>
      <div id="ch-status">
        <span><span class="dot red" id="st-dot"></span><span id="st-txt">Waiting for game</span></span>
        <span style="opacity:0.6">RSHIFT = MENU</span>
      </div>
    `;

    safeAppend(root);

    // === TAB SWITCHING ===
    const tabs = root.querySelectorAll('.ch-tab');
    const panels = root.querySelectorAll('.ch-tab-panel');
    tabs.forEach(tab => {
      tab.addEventListener('click', () => {
        const target = tab.dataset.tab;
        tabs.forEach(t => t.classList.remove('active'));
        panels.forEach(p => p.classList.remove('active'));
        tab.classList.add('active');
        const panel = root.querySelector(`.ch-tab-panel[data-panel="${target}"]`);
        if (panel) panel.classList.add('active');
      });
    });

    // === BINDINGS ===
    const bindToggle = (id, onChange) => {
      const row = document.getElementById(`row-${id}`);
      const tg = document.getElementById(`tg-${id}`);
      if (!row || !tg) return;
      row.addEventListener('click', e => {
        if (e.target.closest('.kb') || e.target.classList.contains('kb-x') || e.target.tagName === 'INPUT') return;
        const res = onChange();
        tg.classList.toggle('on', res);
      });
    };

    bindToggle('ka', () => state.killaura.enabled = !state.killaura.enabled);
    bindToggle('aim', () => state.aimbot.enabled = !state.aimbot.enabled);
    bindToggle('lc', () => toggleClicker('left'));
    bindToggle('rc', () => toggleClicker('right'));
    bindToggle('bhop', () => state.movement.bhop = !state.movement.bhop);
    bindToggle('esp', () => state.visuals.esp = !state.visuals.esp);
    bindToggle('vm', () => state.viewmodel.enabled = !state.viewmodel.enabled);
    bindToggle('spin', () => state.viewmodel.spin = !state.viewmodel.spin);

    let listeningBind = null;
    root.addEventListener('click', e => {
      const unbindEl = e.target.closest('[data-unbind]');
      if (unbindEl) {
        e.stopPropagation();
        const action = unbindEl.dataset.unbind;
        state.binds[action] = '';
        const kbEl = document.getElementById(`bind-${action}`);
        if (kbEl) {
          kbEl.querySelector('.kb-text').innerText = '—';
          const x = kbEl.querySelector('.kb-x'); if (x) x.style.display = 'none';
        }
        if (listeningBind === action) { kbEl?.classList.remove('listening'); listeningBind = null; }
        debouncedSave();
        return;
      }
      const kbEl = e.target.closest('.kb');
      if (!kbEl) return;
      const action = kbEl.dataset.action;
      if (!action) return;
      if (listeningBind) {
        const prev = document.getElementById(`bind-${listeningBind}`);
        if (prev) {
          prev.classList.remove('listening');
          prev.querySelector('.kb-text').innerText = bindDisplay(state.binds[listeningBind]) || '—';
        }
      }
      listeningBind = action;
      kbEl.classList.add('listening');
      kbEl.querySelector('.kb-text').innerText = '…';
    });

    const toggleUiById = (id) => {
      const tg = document.getElementById(`tg-${id}`);
      if (!tg) return;
      let res = false;
      switch (id) {
        case 'ka': res = (state.killaura.enabled = !state.killaura.enabled); break;
        case 'aim': res = (state.aimbot.enabled = !state.aimbot.enabled); break;
        case 'lc': res = toggleClicker('left'); break;
        case 'rc': res = toggleClicker('right'); break;
        case 'bhop': res = (state.movement.bhop = !state.movement.bhop); break;
        case 'esp': res = (state.visuals.esp = !state.visuals.esp); break;
        case 'vm': res = (state.viewmodel.enabled = !state.viewmodel.enabled); break;
      }
      tg.classList.toggle('on', res);
    };

    const preventInput = () => attempt(() => { if (bloxd.noa?.inputs) bloxd.noa.inputs._paused = true; });
    const restoreInput = () => attempt(() => { if (bloxd.noa?.inputs) bloxd.noa.inputs._paused = false; });

    const bindInp = (id, obj, key) => {
      const el = document.getElementById(id);
      if (!el) return;
      el.addEventListener('input', e => { obj[key] = parseFloat(e.target.value) || 0; debouncedSave(); });
      el.addEventListener('focus', preventInput);
      el.addEventListener('blur', restoreInput);
      el.addEventListener('keydown', e => e.stopPropagation(), true);
    };
    bindInp('inp-cmin', state.clicker, 'cpsMin');
    bindInp('inp-cmax', state.clicker, 'cpsMax');

    const bindSlider = (id, cb) => {
      const range = document.getElementById(id);
      const num = document.getElementById(id + '-v');
      if (!range || !num) return;
      const update = (valStr) => {
        let v = parseFloat(valStr);
        if (isNaN(v)) return;
        range.value = v; num.value = v; cb(v); debouncedSave();
      };
      range.addEventListener('input', e => update(e.target.value));
      num.addEventListener('input', e => update(e.target.value));
      num.addEventListener('focus', preventInput);
      num.addEventListener('blur', restoreInput);
      num.addEventListener('keydown', e => e.stopPropagation(), true);
    };

    bindSlider('ka-delay', v => state.killaura.delay = Math.max(0, Math.floor(v)));
    bindSlider('ka-range', v => state.killaura.range = v);
    bindSlider('ka-jitter', v => state.killaura.jitter = v);
    bindSlider('aim-range', v => state.aimbot.range = v);
    bindSlider('aim-smooth', v => state.aimbot.smooth = v);
    bindSlider('aim-delay', v => state.aimbot.delay = v);
    bindSlider('aim-fov', v => state.aimbot.fov = v);
    bindSlider('bhop-chance', v => state.movement.bhopChance = v);
    bindSlider('vm-px', v => state.viewmodel.pos.x = v);
    bindSlider('vm-py', v => state.viewmodel.pos.y = v);
    bindSlider('vm-pz', v => state.viewmodel.pos.z = v);
    bindSlider('vm-rx', v => state.viewmodel.rot.x = v);
    bindSlider('vm-ry', v => state.viewmodel.rot.y = v);
    bindSlider('vm-rz', v => state.viewmodel.rot.z = v);
    bindSlider('spin-sx', v => state.viewmodel.spinSpeed.x = v);
    bindSlider('spin-sy', v => state.viewmodel.spinSpeed.y = v);
    bindSlider('spin-sz', v => state.viewmodel.spinSpeed.z = v);

    // Bind all text inputs to pause game input on focus
    root.querySelectorAll('.ch-text-inp').forEach(el => {
      el.addEventListener('focus', preventInput);
      el.addEventListener('blur', restoreInput);
      el.addEventListener('keydown', e => e.stopPropagation(), true);
    });

    document.getElementById('ch-gen').addEventListener('click', () => {
      if (confirm("Are you sure you want to clear cookies/local storage and generate a new account?")) clearAndReload();
    });

    document.getElementById('ch-inject').addEventListener('click', () => bloxd.init());

    let mini = false, visible = true;
    document.getElementById('ch-min').addEventListener('click', () => {
      mini = !mini; root.classList.toggle('mini', mini);
    });

    document.addEventListener('keydown', e => {
      if (listeningBind) {
        e.preventDefault(); e.stopPropagation();
        const kbEl = document.getElementById(`bind-${listeningBind}`);
        if (e.code === 'Escape') {
          if (kbEl) { kbEl.querySelector('.kb-text').innerText = bindDisplay(state.binds[listeningBind]) || '—'; kbEl.classList.remove('listening'); }
        } else if (e.code === 'Backspace') {
          state.binds[listeningBind] = '';
          if (kbEl) {
            kbEl.querySelector('.kb-text').innerText = '—';
            kbEl.classList.remove('listening');
            const x = kbEl.querySelector('.kb-x'); if (x) x.style.display = 'none';
          }
        } else {
          state.binds[listeningBind] = e.code;
          if (kbEl) {
            kbEl.querySelector('.kb-text').innerText = bindDisplay(e.code);
            kbEl.classList.remove('listening');
            const x = kbEl.querySelector('.kb-x'); if (x) x.style.display = '';
          }
        }
        listeningBind = null;
        debouncedSave();
        return;
      }
      if (e.code === state.binds.menu) {
        visible = !visible;
        root.classList.toggle('hidden', !visible);
      }
      if (document.activeElement?.tagName === 'INPUT') return;
      for (const [id, key] of Object.entries(state.binds)) {
        if (id !== 'menu' && key && e.code === key) toggleUiById(id);
      }
    }, true);

    const hdr = document.getElementById('ch-hdr');
    hdr.addEventListener('mousedown', e => {
      if (e.target !== hdr && e.target.id !== 'ch-title' && !e.target.closest('#ch-title')) return;
      e.preventDefault();
      
      const rect = root.getBoundingClientRect();
      const offsetX = e.clientX - rect.left;
      const offsetY = e.clientY - rect.top;

      const move = ev => {
        const winW = window.innerWidth;
        const winH = window.innerHeight;
        
        let newLeft = ev.clientX - offsetX;
        let newTop = ev.clientY - offsetY;

        if (newLeft < 0) newLeft = 0;
        else if (newLeft + rect.width > winW) newLeft = winW - rect.width;

        if (newTop < 0) newTop = 0;
        else if (newTop + rect.height > winH) newTop = winH - rect.height;

        root.style.left = newLeft + 'px';
        root.style.top = newTop + 'px';
        root.style.right = 'unset';
      };

      const up = () => {
        document.removeEventListener('mousemove', move);
        document.removeEventListener('mouseup', up);
        hdr.style.cursor = 'grab';
      };

      document.addEventListener('mousemove', move);
      document.addEventListener('mouseup', up);
      hdr.style.cursor = 'grabbing';
    });
  }

  let inGame = false;
  function rafLoop(ts) {
    requestAnimationFrame(rafLoop);
    const dot = document.getElementById('st-dot');
    const txt = document.getElementById('st-txt');

    game.updateESP(state.visuals.esp);

    const nowInGame = game.inGame();
    if (nowInGame !== inGame) {
      inGame = nowInGame;
      if (dot && txt) {
        if (inGame) { dot.className = 'dot green'; txt.innerText = 'IN GAME'; }
        else { dot.className = 'dot red'; txt.innerText = 'Waiting for game'; }
      }
    }
    if (!inGame) return;

    const playerIds = vals(game.getIds());
    const selfPos = game.getPos(1);
    const cam = bloxd.noa?.camera;

    // Killaura
    if (state.killaura.enabled && Date.now() - state.killaura._last >= state.killaura.delay) {
      if (selfPos) {
        const kaRangeSq = state.killaura.range * state.killaura.range;
        state.killaura._last = Date.now();
        for (const id of playerIds) {
          if (id == 1) continue;
          const p = game.getPos(id);
          if (!p) continue;
          const dx = p[0] - selfPos[0], dy = p[1] - selfPos[1], dz = p[2] - selfPos[2];
          if (dx * dx + dy * dy + dz * dz <= kaRangeSq) {
            game.doAttack(id);
          }
        }
      }
    }

    // Aimbot
    if (state.aimbot.enabled && Date.now() - state.aimbot._last >= state.aimbot.delay) {
      if (selfPos && cam) {
        let bestTarget = null;
        let minDistSq = state.aimbot.range * state.aimbot.range;
        const fovRad = state.aimbot.fov * (Math.PI / 180);
        
        for (const id of playerIds) {
          if (id == 1) continue;
          const p = game.getPos(id);
          if (!p) continue;
          
          const dx = p[0] - selfPos[0], dy = p[1] - selfPos[1], dz = p[2] - selfPos[2];
          const dSq = dx * dx + dy * dy + dz * dz;

          if (dSq < minDistSq) {
            // --- FOV CHECK START ---
            const targetYaw = Math.atan2(dx, dz);
            const diff = Math.atan2(Math.sin(targetYaw - cam.heading), Math.cos(targetYaw - cam.heading));
            
            if (Math.abs(diff) <= fovRad / 2) {
              minDistSq = dSq;
              bestTarget = p;
            }
            // --- FOV CHECK END ---
          }
        }
        
        if (bestTarget) {
          const dx = bestTarget[0] - selfPos[0];
          const dy = (bestTarget[1] + 1.5) - (selfPos[1] + 1.5);
          const dz = bestTarget[2] - selfPos[2];
          const yaw = Math.atan2(dx, dz);
          const pitch = -Math.atan2(dy, Math.hypot(dx, dz));
          
          const smooth = state.aimbot.smooth;
          cam.heading += Math.atan2(Math.sin(yaw - cam.heading), Math.cos(yaw - cam.heading)) * smooth;
          cam.pitch += (Math.max(-1.57, Math.min(1.57, pitch)) - cam.pitch) * smooth;
          state.aimbot._last = Date.now();
        }
      }
    };

    // BHOP
    const inputs = bloxd.noa?.inputs?.state;
    const mov = game.getMovement(1);
    if (state.movement.bhop && inputs && mov) {
      const isMoving = inputs.forward || inputs.backward || inputs.left || inputs.right;
      const onGround = attempt(() => mov.isOnGround());
      if (onGround && isMoving && Math.random() * 100 <= state.movement.bhopChance) {
        inputs.jump = true;
      } else {
        inputs.jump = false;
      }
    }

    // ViewModel
    if (state.viewmodel.enabled) {
      const item = game.getHeld(1);
      if (item) {
        const vdt = state.viewmodel._lastTime ? (ts - state.viewmodel._lastTime) / 1000 : 0;
        state.viewmodel._lastTime = ts;

        if (state.viewmodel.spin && item?.typeObj?.constructor?.name === "Tool") {
          state.viewmodel.spinAngle.x += state.viewmodel.spinSpeed.x * vdt;
          state.viewmodel.spinAngle.y += state.viewmodel.spinSpeed.y * vdt;
          state.viewmodel.spinAngle.z += state.viewmodel.spinSpeed.z * vdt;
        }

        if (item.firstPersonPosOffset) {
          item.firstPersonPosOffset.x = state.viewmodel.pos.x;
          item.firstPersonPosOffset.y = state.viewmodel.pos.y;
          item.firstPersonPosOffset.z = state.viewmodel.pos.z;
        }

        if (item.firstPersonRotation && item?.typeObj?.constructor?.name === "Tool") {
          item.firstPersonRotation.x = state.viewmodel.rot.x + (state.viewmodel.spin ? state.viewmodel.spinAngle.x : 0);
          item.firstPersonRotation.y = state.viewmodel.rot.y + (state.viewmodel.spin ? state.viewmodel.spinAngle.y : 0);
          item.firstPersonRotation.z = state.viewmodel.rot.z + (state.viewmodel.spin ? state.viewmodel.spinAngle.z : 0);
        }
      }
    }
  }

  let uiBuilt = false;
  function initUI() {
    if (!uiBuilt) { uiBuilt = true; buildUI(); }
  }

  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initUI);
  else initUI();

  bloxd.init();

  let coreStarted = false;
  const poll = setInterval(() => {
    if (!uiBuilt) initUI();
    if (window.noa && !coreStarted) {
      coreStarted = true;
      bloxd.noa = window.noa;
      requestAnimationFrame(rafLoop);
      clearInterval(poll);
    }
  }, 250);
})();