Qwen Chat: One-Click Toggle Overview Button

One-Click Toggle Overview Button

// ==UserScript==
// @name         Qwen Chat: One-Click Toggle Overview Button
// @name:ru      Qwen Chat: Кнопка для мгновенного включения/выключения обзора
// @namespace    http://tampermonkey.net/
// @description  One-Click Toggle Overview Button
// @description:ru Кнопка для мгновенного включения/выключения обзора
// @version      1.1
// @match        https://chat.qwen.ai/*
// @grant        none
// @run-at       document-idle
// @license MIT
// ==/UserScript==

(function () {
  'use strict';

  const CONTEXT_BTN_ID       = 'chat-context-menu-button';
  const MENU_SELECTOR        = 'div[role="menu"][data-state="open"]';
  const OVERVIEW_ICON_CLASS  = 'icon-map-01';
  const BUTTON_ID            = 'direct-overview-btn';

  function isOverviewOpen() {
    return !!document.querySelector('[data-testid="svelte-flow__wrapper"]');
  }

  async function openOverviewIfNeeded() {
    if ( isOverviewOpen() ) return false;
    const trigger = document.getElementById(CONTEXT_BTN_ID);
    if (!trigger) return false;
    trigger.click();
    await new Promise(r => setTimeout(r, 100));
    const menu = document.querySelector(MENU_SELECTOR);
    if (!menu) return false;
    // find the item by its icon
    const icon = menu.querySelector(`.${OVERVIEW_ICON_CLASS}`);
    const item = icon?.closest('[role="menuitem"]');
    if (!item) return false;
    // use whatever text the menu item has
    const label = item.textContent.trim();
    const btn   = document.getElementById(BUTTON_ID);
    if (btn) btn.querySelector('span').textContent = label;
    item.click();
    // wait for panel
    for (let i=0; i<10 && !isOverviewOpen(); i++) {
      await new Promise(r => setTimeout(r, 100));
    }
    return isOverviewOpen();
  }

  function toggleOverviewPane() {
    const pane = document
      .querySelector('[data-testid="svelte-flow__wrapper"]')
      ?.closest('[data-pane]');
    if (!pane) return;
    pane.style.display = pane.style.display === 'none' ? '' : 'none';
  }

  function insertButton() {
    const op = document.querySelector('.operationBtn');
    if (!op || document.getElementById(BUTTON_ID)) return;
    op.insertAdjacentHTML('beforeend', `
      <div id="${BUTTON_ID}"
           class="flex cursor-pointer items-center gap-2 rounded-md px-3 py-2 text-sm
                  hover:bg-gray-50 dark:hover:bg-gray-800"
           title="Toggle Overview">
        <i class="iconfont leading-none ${OVERVIEW_ICON_CLASS}"></i>
        <span></span>
      </div>`);
    document.getElementById(BUTTON_ID)
      .addEventListener('click', async () => {
        if (isOverviewOpen()) {
          toggleOverviewPane();
        } else {
          await openOverviewIfNeeded();
        }
      });
  }

  insertButton();
  new MutationObserver(insertButton)
    .observe(document.body, { childList: true, subtree: true });

})();