Model Selector for lmarena (Text-Based + Debug)

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

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

})();