Orion iOS Eruda Console Toggle

Persistent, stronger Eruda launcher for Orion iOS with always-on mode and blocker-resilient loading fallbacks.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Advertisement:

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

Advertisement:

// ==UserScript==
// @name         Orion iOS Eruda Console Toggle
// @namespace    https://littlelooney.example/orion-eruda
// @version      2026-05-24.2
// @description  Persistent, stronger Eruda launcher for Orion iOS with always-on mode and blocker-resilient loading fallbacks.
// @author       LittleLooney + OpenAI
// @match        *://*/*
// @require      https://cdn.jsdelivr.net/npm/[email protected]/eruda.min.js
// @grant        none
// @run-at       document-start
// ==/UserScript==

(() => {
  'use strict';

  const STORAGE_KEY = 'orion-eruda-toggle-position';
  const AUTO_KEY = 'orion-eruda-auto-open';
  const BTN_ID = 'orion-eruda-toggle-btn';
  const CDN_FALLBACKS = [
    'https://cdn.jsdelivr.net/npm/[email protected]/eruda.min.js',
    'https://unpkg.com/[email protected]/eruda.min.js'
  ];

  let erudaReady = false;
  let erudaVisible = false;

  function loadPosition() {
    try {
      const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
      if (typeof saved.left === 'number' && typeof saved.top === 'number') return saved;
    } catch {}
    return { left: 14, top: 86 };
  }

  function savePosition(left, top) {
    localStorage.setItem(STORAGE_KEY, JSON.stringify({ left, top }));
  }

  function isAutoOpenEnabled() {
    return localStorage.getItem(AUTO_KEY) !== '0';
  }

  function setAutoOpenEnabled(enabled) {
    localStorage.setItem(AUTO_KEY, enabled ? '1' : '0');
  }

  function clamp(value, min, max) {
    return Math.min(Math.max(value, min), max);
  }

  function updateButtonStyle(btn, pos) {
    const maxLeft = Math.max(8, window.innerWidth - btn.offsetWidth - 8);
    const maxTop = Math.max(8, window.innerHeight - btn.offsetHeight - 8);
    const left = clamp(pos.left, 8, maxLeft);
    const top = clamp(pos.top, 8, maxTop);
    btn.style.left = `${left}px`;
    btn.style.top = `${top}px`;
    savePosition(left, top);
  }

  function loadScript(src) {
    return new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = src;
      script.async = true;
      script.onload = resolve;
      script.onerror = () => reject(new Error(`Failed to load ${src}`));
      (document.head || document.documentElement).appendChild(script);
    });
  }

  async function ensureEruda() {
    if (!window.eruda) {
      for (const src of CDN_FALLBACKS) {
        try {
          await loadScript(src);
          if (window.eruda) break;
        } catch {}
      }
    }
    if (!window.eruda) throw new Error('Eruda unavailable (possibly blocked by network/content blocker).');

    if (!erudaReady) {
      window.eruda.init({ useShadowDom: false, autoScale: true });
      erudaReady = true;
    }
  }

  function setButtonState(btn, text) {
    btn.textContent = text;
    btn.setAttribute('aria-label', text);
  }

  async function openEruda(btn) {
    await ensureEruda();
    window.eruda.show();
    erudaVisible = true;
    setButtonState(btn, isAutoOpenEnabled() ? 'Console: ON • AUTO' : 'Console: ON');
  }

  function closeEruda(btn) {
    if (window.eruda) window.eruda.hide();
    erudaVisible = false;
    setButtonState(btn, isAutoOpenEnabled() ? 'Console: OFF • AUTO' : 'Console: OFF');
  }

  async function toggleEruda(btn) {
    btn.disabled = true;
    try {
      if (erudaVisible) closeEruda(btn); else await openEruda(btn);
    } catch (err) {
      console.error('[Orion Eruda]', err);
      setButtonState(btn, 'Console: ERR');
      setTimeout(() => setButtonState(btn, erudaVisible ? 'Console: ON' : 'Console: OFF'), 1800);
    } finally {
      btn.disabled = false;
    }
  }

  function addAutoToggle(btn) {
    btn.addEventListener('contextmenu', (e) => {
      e.preventDefault();
      const next = !isAutoOpenEnabled();
      setAutoOpenEnabled(next);
      if (erudaVisible) setButtonState(btn, next ? 'Console: ON • AUTO' : 'Console: ON');
      else setButtonState(btn, next ? 'Console: OFF • AUTO' : 'Console: OFF');
    });
  }

  function makeDraggable(btn) {
    let dragging = false, moved = false, startX = 0, startY = 0, originLeft = 0, originTop = 0;
    const onMove = (x, y) => {
      if (!dragging) return;
      const dx = x - startX, dy = y - startY;
      moved = moved || Math.abs(dx) > 4 || Math.abs(dy) > 4;
      updateButtonStyle(btn, { left: originLeft + dx, top: originTop + dy });
    };

    btn.addEventListener('touchstart', (e) => {
      const t = e.touches[0]; if (!t) return;
      dragging = true; moved = false; startX = t.clientX; startY = t.clientY;
      originLeft = parseFloat(btn.style.left) || 0; originTop = parseFloat(btn.style.top) || 0;
    }, { passive: true });
    btn.addEventListener('touchmove', (e) => { const t = e.touches[0]; if (t) onMove(t.clientX, t.clientY); }, { passive: true });
    btn.addEventListener('touchend', () => { dragging = false; }, { passive: true });
    btn.addEventListener('mousedown', (e) => {
      dragging = true; moved = false; startX = e.clientX; startY = e.clientY;
      originLeft = parseFloat(btn.style.left) || 0; originTop = parseFloat(btn.style.top) || 0; e.preventDefault();
    });
    window.addEventListener('mousemove', (e) => onMove(e.clientX, e.clientY));
    window.addEventListener('mouseup', () => { dragging = false; });

    btn.addEventListener('click', (e) => {
      if (moved) { e.preventDefault(); e.stopPropagation(); moved = false; return; }
      toggleEruda(btn);
    });
  }

  function attachKeyboardShortcut(btn) {
    window.addEventListener('keydown', (e) => {
      if (e.altKey && e.key.toLowerCase() === 'd') {
        e.preventDefault();
        toggleEruda(btn);
      }
    });
  }

  function injectUi() {
    if (document.getElementById(BTN_ID)) return document.getElementById(BTN_ID);
    const btn = document.createElement('button');
    btn.id = BTN_ID;
    btn.type = 'button';
    setButtonState(btn, isAutoOpenEnabled() ? 'Console: OFF • AUTO' : 'Console: OFF');
    btn.title = 'Tap: toggle console. Drag: move. Long-press/right-click: toggle AUTO mode.';
    Object.assign(btn.style, { position:'fixed', zIndex:'2147483647', padding:'10px 12px', borderRadius:'12px', border:'1px solid rgba(255,255,255,0.25)', background:'rgba(16,16,20,0.85)', color:'#f5f5f5', fontFamily:'-apple-system, BlinkMacSystemFont, Segoe UI, sans-serif', fontSize:'13px', fontWeight:'600', boxShadow:'0 6px 24px rgba(0,0,0,0.25)', cursor:'pointer', userSelect:'none', WebkitUserSelect:'none', opacity:'0.95' });
    (document.documentElement || document.body).appendChild(btn);
    updateButtonStyle(btn, loadPosition());
    makeDraggable(btn);
    attachKeyboardShortcut(btn);
    addAutoToggle(btn);
    window.addEventListener('resize', () => updateButtonStyle(btn, { left: parseFloat(btn.style.left) || 8, top: parseFloat(btn.style.top) || 8 }));
    return btn;
  }

  async function boot() {
    const btn = injectUi();
    if (isAutoOpenEnabled()) {
      try { await openEruda(btn); } catch (e) { console.warn('[Orion Eruda] Auto-open failed', e); }
    }
  }

  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot, { once: true });
  else boot();
})();