Orion iOS Eruda Console Toggle

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

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Advertisement:

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

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