Model Selector for lmarena (Text-Based + Debug)

Auto-select model and auto-submit prompt on lmarena (robust + debug)

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Model Selector for lmarena (Text-Based + Debug)
// @namespace    http://tampermonkey.net/
// @version      2026-01-04
// @description  Auto-select model and auto-submit prompt on lmarena (robust + debug)
// @author       Animesh Dhakal
// @match        https://lmarena.ai/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  /* ===================== DEBUG HELPERS ===================== */

  const LOG  = (...a) => console.log('%c[LM-Arena]', 'color:#4CAF50;font-weight:bold', ...a);
  const WARN = (...a) => console.warn('%c[LM-Arena]', 'color:#FFC107;font-weight:bold', ...a);
  const ERR  = (...a) => console.error('%c[LM-Arena]', 'color:#F44336;font-weight:bold', ...a);

  /* ===================== UTILITIES ===================== */

  function waitForElm(selector, timeout = 15000) {
    LOG(`Waiting for: ${selector}`);
    return new Promise((resolve, reject) => {
      const found = document.querySelector(selector);
      if (found) return resolve(found);

      const obs = new MutationObserver(() => {
        const el = document.querySelector(selector);
        if (el) {
          obs.disconnect();
          resolve(el);
        }
      });

      obs.observe(document.body, { childList: true, subtree: true });

      setTimeout(() => {
        obs.disconnect();
        reject(new Error(`Timeout: ${selector}`));
      }, timeout);
    });
  }

  const sleep = ms => new Promise(r => setTimeout(r, ms));

  function isDisabled(btn) {
    return (
      btn.disabled ||
      btn.getAttribute('aria-disabled') === 'true' ||
      btn.classList.contains('disabled')
    );
  }

  async function waitUntilEnabled(btn, timeout = 10000) {
    const start = Date.now();
    while (isDisabled(btn)) {
      if (Date.now() - start > timeout) {
        throw new Error('Submit button never enabled');
      }
      await sleep(100);
    }
    return btn;
  }

  function setReactInputValue(input, value) {
    const proto = Object.getPrototypeOf(input);
    const setter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;

    if (setter) setter.call(input, value);
    else input.value = value;

    input.dispatchEvent(new Event('input', { bubbles: true }));
    input.dispatchEvent(new Event('change', { bubbles: true }));
  }

  /* ===================== MAIN LOGIC ===================== */

  async function run() {
    LOG('Script started');

    const params = new URLSearchParams(window.location.search);
    let model = params.get('model');
    const query = params.get('query');
    const chatModality = params.get('chat-modality') || 'direct';

    if (!model) {
      model = chatModality === 'direct'
        ? 'gpt'
        : 'ppl-sonar-pro-high';
      LOG('No model param, defaulting to:', model);
    }

    const modelLower = model.toLowerCase();
    LOG('Target model text:', modelLower);

    /* wait for message box (SPA-safe entry point) */
    await waitForElm('[name="message"]');

    /* open model combobox */
    const comboboxes = [...document.querySelectorAll("button[role='combobox']")];
    LOG(`Comboboxes found: ${comboboxes.length}`);

    const modelCombobox = comboboxes[1];

    if (!modelCombobox) {
      ERR('Model combobox not found');
      return;
    }

    modelCombobox.click();
    LOG('Model dropdown opened');

    /* wait for options to appear */
    await waitForElm('div[role="option"]');

    /* select model by VISIBLE TEXT (FIX) */
    const options = [...document.querySelectorAll('div[role="option"]')];
    LOG(`Model options found: ${options.length}`);

    const modelOption = options.find(opt =>
      opt.textContent.toLowerCase().includes(modelLower)
    );

    if (!modelOption) {
      ERR(
        'Model not found by text. Available models:',
        options.map(o => o.textContent.trim())
      );
      return;
    }

    modelOption.scrollIntoView({ block: 'center' });
    modelOption.click();
    LOG('Model selected:', modelOption.textContent.trim());

    if (!query) {
      WARN('No query provided — stopping after model selection');
      return;
    }

    /* fill message */
    const messageInput = await waitForElm('[name="message"]');
    setReactInputValue(messageInput, query);
    LOG('Message filled');

    /* submit */
    const submitButton = await waitForElm('button[type="submit"]');
    await waitUntilEnabled(submitButton);
    submitButton.click();

    LOG('Prompt submitted successfully');
  }

  /* ===================== SPA RE-RUN SUPPORT ===================== */

  let lastUrl = location.href;
  setInterval(() => {
    if (location.href !== lastUrl) {
      lastUrl = location.href;
      LOG('URL changed, re-running');
      run().catch(e => ERR(e));
    }
  }, 1000);

  /* ===================== BOOT ===================== */

  run().catch(e => ERR('Fatal error:', e));

})();