Gemini Prompt Queue

A userscript to manage a queue of prompts for Gemini.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

Advertisement:

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

Advertisement:

// ==UserScript==
// @name         Gemini Prompt Queue
// @description  A userscript to manage a queue of prompts for Gemini.
// @author       nihaltp
// @namespace    https://github.com/nihaltp/uscripts
// @supportURL   https://github.com/nihaltp/uscripts/issues/new?title=%5BBUG%5D%20Gemini%20Prompt%20Queue%20dist%2Fgemini.user.js&body=File%3A%20AI%20Queue%2Fdist%2Fgemini.user.js%0A%0ADescribe%20issue%20here...
// @homepageURL  https://github.com/nihaltp/uscripts
// @homepage     https://github.com/nihaltp/uscripts
// @license      MIT
// @match        https://gemini.google.com/app
// @match        https://gemini.google.com/app/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=gemini.google.com
// @version      3.0.23
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(() => {
  // AI Queue/core/state.js
  var queueState = {
    queue: [],
    failedQueue: [],
    running: false,
    editingId: null,
    draggedId: null,
    awaitingChatScopeSync: false,
  };
  function resetQueueState({ includeFailedQueue = false } = {}) {
    queueState.queue.length = 0;
    if (includeFailedQueue) {
      queueState.failedQueue.length = 0;
    }
    queueState.running = false;
    queueState.editingId = null;
    queueState.draggedId = null;
    queueState.awaitingChatScopeSync = false;
  }

  // AI Queue/core/logging.js
  window.aiQueueDebug = false;
  function isDebugEnabled() {
    return Boolean(globalThis.aiQueueDebug);
  }
  function log(...args) {
    const force = typeof args[args.length - 1] === 'boolean' ? args.pop() : false;
    if (!force && !isDebugEnabled()) return;
    console.log('[AI QUEUE]', ...args);
  }
  function error(...args) {
    console.error('[AI QUEUE]', ...args);
  }
  function formatError(err) {
    if (err instanceof Error) {
      return err.message;
    }
    if (typeof err === 'string') {
      return err;
    }
    if (err && typeof err.message === 'string') {
      return err.message;
    }
    try {
      return JSON.stringify(err);
    } catch {
      return String(err);
    }
  }
  function throwError(...args) {
    error(...args);
    throw new Error(args.join(' '));
  }

  // AI Queue/core/utils.js
  function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
  function isAttached(element) {
    return !!element && document.contains(element);
  }
  function isVisible(element) {
    if (!isAttached(element)) return false;
    if (!(element instanceof HTMLElement)) return false;
    const style = window.getComputedStyle(element);
    if (!style) return false;
    if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
      return false;
    }
    const rect = element.getBoundingClientRect();
    if (rect.width <= 0 || rect.height <= 0) return false;
    return (
      rect.bottom > 0 &&
      rect.right > 0 &&
      rect.top < window.innerHeight &&
      rect.left < window.innerWidth
    );
  }
  function isActionButtonElement(element) {
    return (
      !!element &&
      element instanceof HTMLElement &&
      element.matches('button, [role="button"], input[type="button"], input[type="submit"]')
    );
  }
  function isOwnMutation(target) {
    return (
      !!target &&
      (target.closest?.('#pq-panel') || target.closest?.('.pq-toolbar') || target.id === 'pq-panel')
    );
  }

  // AI Queue/styles/ui.css
  var ui_default =
    '@keyframes pq-pulse {\r\n  0% {\r\n    transform: scale(1);\r\n    opacity: 1;\r\n  }\r\n\r\n  50% {\r\n    transform: scale(1.06);\r\n    opacity: 0.75;\r\n  }\r\n\r\n  100% {\r\n    transform: scale(1);\r\n    opacity: 1;\r\n  }\r\n}';

  // AI Queue/core/ui.js
  var repairTimer = null;
  var lastRepairAt = 0;
  var repairing = false;
  var urlWatcher = null;
  var lastKnownUrl = location.href;
  var mutationObserver = null;
  function ensureToolbarStyles() {
    if (document.querySelector('#pq-styles')) return;
    const style = document.createElement('style');
    style.id = 'pq-styles';
    style.textContent = ui_default;
    document.head.appendChild(style);
  }
  function getPanel() {
    return document.querySelector('#pq-panel');
  }
  function ensurePanelAttached(panel = getPanel()) {
    const currentPanel = getPanel() || panel || null;
    if (!currentPanel) return false;
    const root = document.documentElement || document.body;
    if (!root) return false;
    if (!root.contains(currentPanel)) {
      root.appendChild(currentPanel);
    }
    log('attached', root.contains(currentPanel));
    log('parent node', currentPanel.parentNode);
    return true;
  }
  function showPanel(createPanel) {
    log('TOGGLE PANEL');
    let panel = getPanel();
    if (!panel) {
      createPanel?.();
      panel = getPanel();
    }
    if (!panel) {
      error('panel missing');
      return;
    }
    const isHidden = panel.hidden || getComputedStyle(panel).display === 'none';
    if (isHidden) {
      if (!ensurePanelAttached(panel)) {
        error('failed to attach panel');
        return;
      }
      panel.hidden = false;
      panel.style.setProperty('display', 'block', 'important');
      panel.style.setProperty('visibility', 'visible', 'important');
      panel.style.setProperty('opacity', '1', 'important');
      panel.style.setProperty('pointer-events', 'auto', 'important');
      panel.style.setProperty('z-index', '2147483647', 'important');
      log('final state', {
        display: getComputedStyle(panel).display,
        visibility: getComputedStyle(panel).visibility,
        rect: panel.getBoundingClientRect(),
      });
      return;
    }
    panel.hidden = true;
    panel.style.setProperty('display', 'none', 'important');
    panel.style.setProperty('pointer-events', 'none', 'important');
    log('panel hidden');
  }
  function hidePanel(panel = getPanel()) {
    if (!panel) return;
    panel.hidden = true;
    panel.style.setProperty('display', 'none', 'important');
    panel.style.setProperty('pointer-events', 'none', 'important');
    log('panel hidden');
  }
  function updateToolbarButton(toolbarButton, queue, running) {
    if (!toolbarButton || !isAttached(toolbarButton)) return;
    const count = queue.length;
    toolbarButton.textContent = count > 0 ? `Queue (${count})` : 'Queue';
    toolbarButton.style.animation = running ? 'pq-pulse 1.2s infinite' : '';
    toolbarButton.style.opacity = running ? '1' : count > 0 ? '1' : '0.8';
  }
  function repairUi(
    reason = 'repair',
    createPanel,
    setupPanelEvents,
    setupPanelDrag2,
    ensureToolbarButton
  ) {
    if (repairing) return;
    repairing = true;
    try {
      log('repair', reason);
      createPanel();
      setupPanelEvents?.();
      setupPanelDrag2?.();
      ensureToolbarButton?.();
    } finally {
      repairing = false;
    }
  }
  function requestRepair(
    reason = 'repair',
    createPanel,
    setupPanelEvents,
    setupPanelDrag2,
    ensureToolbarButton
  ) {
    const now = Date.now();
    const delay = Math.max(0, 2e3 - (now - lastRepairAt));
    if (repairTimer) {
      clearTimeout(repairTimer);
    }
    repairTimer = setTimeout(() => {
      lastRepairAt = Date.now();
      repairUi(reason, createPanel, setupPanelEvents, setupPanelDrag2, ensureToolbarButton);
    }, delay);
  }
  function startDomObserver(
    createPanel,
    setupPanelEvents,
    setupPanelDrag2,
    ensureToolbarButton,
    isOwnMutationOverride
  ) {
    if (mutationObserver) return;
    const target = document.body || document.documentElement;
    if (!target) return;
    mutationObserver = new MutationObserver((mutations) => {
      for (const mutation of mutations) {
        if (mutation.type === 'childList') {
          const hasExternalChange = [...mutation.addedNodes, ...mutation.removedNodes].some(
            (node) => node && !isOwnMutation(node)
          );
          if (hasExternalChange) {
            requestRepair(
              'dom-mutation',
              createPanel,
              setupPanelEvents,
              setupPanelDrag2,
              ensureToolbarButton
            );
            break;
          }
        }
        if (mutation.type === 'attributes') {
          if (!isOwnMutationOverride?.(mutation.target)) {
            requestRepair(
              'attribute-mutation',
              createPanel,
              setupPanelEvents,
              setupPanelDrag2,
              ensureToolbarButton
            );
            break;
          }
        }
      }
    });
    mutationObserver.observe(target, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['aria-busy', 'aria-disabled', 'disabled'],
    });
  }
  function patchHistoryMethod(methodName) {
    const original = history[methodName];
    if (typeof original !== 'function' || original.__pqPatched) return;
    const patched = function (...args) {
      const result = original.apply(this, args);
      requestRepair('history-' + methodName);
      return result;
    };
    patched.__pqPatched = true;
    history[methodName] = patched;
  }
  function startUrlWatcher(
    createPanel,
    setupPanelEvents,
    setupPanelDrag2,
    ensureToolbarButton,
    onUrlChange
  ) {
    const handleUrlChange = (reason, previousUrl, currentUrl) => {
      onUrlChange?.(previousUrl, currentUrl, reason);
      requestRepair(reason, createPanel, setupPanelEvents, setupPanelDrag2, ensureToolbarButton);
    };
    patchHistoryMethod('pushState');
    patchHistoryMethod('replaceState');
    window.addEventListener('popstate', () => {
      const previousUrl = lastKnownUrl;
      lastKnownUrl = location.href;
      handleUrlChange('popstate', previousUrl, lastKnownUrl);
    });
    window.addEventListener('hashchange', () => {
      const previousUrl = lastKnownUrl;
      lastKnownUrl = location.href;
      handleUrlChange('hashchange', previousUrl, lastKnownUrl);
    });
    if (urlWatcher) clearInterval(urlWatcher);
    urlWatcher = setInterval(() => {
      if (location.href !== lastKnownUrl) {
        const previousUrl = lastKnownUrl;
        lastKnownUrl = location.href;
        handleUrlChange('url-change', previousUrl, lastKnownUrl);
      }
    }, 1e3);
  }

  // AI Queue/core/panel.js
  function createBasePanel(titleText, includeFailedList = false) {
    log('createBasePanel called');
    let panel = document.querySelector('#pq-panel');
    if (!panel) {
      panel = document.createElement('div');
      panel.id = 'pq-panel';
      Object.assign(panel.style, {
        position: 'fixed',
        top: '80px',
        left: '24px',
        bottom: 'auto',
        right: 'auto',
        width: '320px',
        minHeight: '200px',
        maxHeight: '70vh',
        overflowY: 'auto',
        background: '#202123',
        color: 'white',
        border: '1px solid #444',
        borderRadius: '16px',
        padding: '12px',
        zIndex: '2147483647',
        boxShadow: '0 10px 40px rgba(0,0,0,0.6)',
        display: 'none',
        transform: 'none',
      });
      const title = document.createElement('div');
      title.style.display = 'flex';
      title.style.alignItems = 'center';
      title.style.justifyContent = 'space-between';
      title.style.gap = '12px';
      title.style.fontSize = '18px';
      title.style.fontWeight = 'bold';
      title.style.marginBottom = '10px';
      const titleLabel = document.createElement('span');
      titleLabel.textContent = titleText;
      const closeBtn = document.createElement('button');
      closeBtn.id = 'pq-close';
      closeBtn.type = 'button';
      closeBtn.textContent = 'Close';
      Object.assign(closeBtn.style, {
        flexShrink: '0',
        padding: '4px 10px',
        borderRadius: '9999px',
        border: '1px solid #555',
        background: '#2a2a2a',
        color: '#fff',
        cursor: 'pointer',
      });
      closeBtn.addEventListener('click', () => hidePanel(panel));
      title.appendChild(titleLabel);
      title.appendChild(closeBtn);
      const textarea = document.createElement('textarea');
      textarea.id = 'pq-input';
      textarea.placeholder = 'Enter prompt...';
      Object.assign(textarea.style, {
        width: '100%',
        height: '80px',
        resize: 'vertical',
        color: '#fff',
        background: '#222',
        border: '1px solid #444',
        borderRadius: '6px',
        padding: '8px',
        boxSizing: 'border-box',
      });
      const addBtn = document.createElement('button');
      addBtn.id = 'pq-add';
      addBtn.style.marginTop = '10px';
      addBtn.style.width = '100%';
      addBtn.textContent = 'Add To Queue';
      const manageChatsBtn = document.createElement('button');
      manageChatsBtn.id = 'pq-manage-chats';
      manageChatsBtn.style.marginTop = '10px';
      manageChatsBtn.style.width = '100%';
      manageChatsBtn.textContent = 'Manage Chat Prompts';
      const startBtn = document.createElement('button');
      startBtn.id = 'pq-start';
      startBtn.style.marginTop = '10px';
      startBtn.style.width = '100%';
      startBtn.textContent = 'Start Queue';
      const status = document.createElement('div');
      status.id = 'pq-status';
      status.style.marginTop = '10px';
      status.textContent = 'Idle';
      const list = document.createElement('ol');
      list.id = 'pq-list';
      list.style.marginTop = '10px';
      list.style.paddingLeft = '20px';
      panel.appendChild(title);
      panel.appendChild(textarea);
      panel.appendChild(addBtn);
      panel.appendChild(manageChatsBtn);
      panel.appendChild(startBtn);
      panel.appendChild(status);
      panel.appendChild(list);
      if (includeFailedList) {
        const failedTitle = document.createElement('div');
        failedTitle.id = 'pq-failed-title';
        failedTitle.style.marginTop = '12px';
        failedTitle.style.fontSize = '13px';
        failedTitle.style.opacity = '0.8';
        failedTitle.textContent = 'Failed Prompts';
        const failedList = document.createElement('ol');
        failedList.id = 'pq-failed-list';
        failedList.style.marginTop = '6px';
        failedList.style.paddingLeft = '20px';
        panel.appendChild(failedTitle);
        panel.appendChild(failedList);
      }
      const root = document.documentElement || document.body;
      if (root) {
        root.appendChild(panel);
      }
    } else {
      ensurePanelAttached(panel);
    }
    log('createBasePanel panel', panel);
    setTimeout(() => {
      log('panel element', panel);
      log('computed display', getComputedStyle(panel).display);
      log('computed visibility', getComputedStyle(panel).visibility);
      log('rect', panel.getBoundingClientRect());
      log('parent', panel.parentElement);
    }, 0);
    return panel;
  }

  // AI Queue/core/queue.js
  function deleteQueueItem(id, queue, renderQueue, saveQueue2) {
    const index = queue.findIndex((item) => item.id === id);
    if (index === -1) {
      log('Item to delete not found in queue:', id);
      return;
    }
    queue.splice(index, 1);
    renderQueue();
    saveQueue2?.();
  }
  function editQueueItem(id, queue, updateUI) {
    const item = queue.find((item2) => item2.id === id);
    if (!item) {
      log('Item to edit not found in queue:', id);
      return;
    }
    updateUI(id, item.prompt);
  }
  function moveQueueItem(fromId, toId, queue, renderQueue, saveQueue2) {
    const fromIndex = queue.findIndex((item) => item.id === fromId);
    const toIndex = queue.findIndex((item) => item.id === toId);
    if (fromIndex === -1 || toIndex === -1) return;
    const [movedItem] = queue.splice(fromIndex, 1);
    queue.splice(toIndex, 0, movedItem);
    renderQueue();
    saveQueue2?.();
  }
  function setStatus(panel, text) {
    if (!panel) return;
    const status = panel.querySelector('#pq-status');
    if (status) {
      status.textContent = text;
    }
  }

  // AI Queue/core/queue-ui.js
  function createQueueItemElement(item, { renderQueue, saveQueue: saveQueue2 }) {
    const li = document.createElement('li');
    li.style.marginBottom = '10px';
    li.draggable = false;
    const row = document.createElement('div');
    row.style.display = 'flex';
    row.style.gap = '6px';
    row.style.alignItems = 'flex-start';
    const text = document.createElement('div');
    text.textContent = item.prompt;
    text.style.flex = '1';
    text.style.wordBreak = 'break-word';
    text.style.fontSize = '14px';
    const editBtn = document.createElement('button');
    editBtn.textContent = '\u{1F589}';
    editBtn.title = 'Edit';
    editBtn.style.cursor = 'pointer';
    editBtn.style.color = '#7dd3fc';
    editBtn.style.display = 'none';
    const deleteBtn = document.createElement('button');
    deleteBtn.textContent = '\u2715';
    deleteBtn.title = 'Delete';
    deleteBtn.style.cursor = 'pointer';
    deleteBtn.style.color = '#ff6b6b';
    deleteBtn.style.display = 'none';
    row.appendChild(text);
    row.appendChild(editBtn);
    row.appendChild(deleteBtn);
    const dragHandle = document.createElement('span');
    dragHandle.textContent = '\u2630';
    dragHandle.title = 'Drag to reorder';
    dragHandle.style.cursor = 'grab';
    dragHandle.style.userSelect = 'none';
    dragHandle.style.alignSelf = 'center';
    dragHandle.style.marginLeft = '6px';
    dragHandle.style.display = 'none';
    dragHandle.addEventListener('dragstart', (e) => {
      queueState.draggedId = item.id;
      try {
        e.dataTransfer.setData('text/plain', item.id);
        e.dataTransfer.effectAllowed = 'move';
      } catch (err) {
        error('Drag start dataTransfer error:', err);
      }
      li.style.opacity = '0.6';
    });
    dragHandle.addEventListener('dragend', () => {
      queueState.draggedId = null;
      li.style.opacity = '';
    });
    row.appendChild(dragHandle);
    li.appendChild(row);
    li.addEventListener('mouseenter', () => {
      editBtn.style.display = 'inline-block';
      deleteBtn.style.display = 'inline-block';
      dragHandle.style.display = 'inline-block';
    });
    li.addEventListener('mouseleave', () => {
      if (queueState.editingId === item.id) return;
      editBtn.style.display = 'none';
      deleteBtn.style.display = 'none';
      if (queueState.draggedId === item.id) return;
      dragHandle.style.display = 'none';
    });
    li.addEventListener('dragover', (e) => {
      e.preventDefault();
      try {
        e.dataTransfer.dropEffect = 'move';
      } catch (err) {
        error('Drag over dataTransfer error:', err);
      }
      li.style.borderTop = '2px solid #7dd3fc';
    });
    li.addEventListener('dragleave', () => {
      li.style.borderTop = '';
    });
    li.addEventListener('drop', (e) => {
      e.preventDefault();
      li.style.borderTop = '';
      const draggedId =
        queueState.draggedId ||
        (e.dataTransfer && e.dataTransfer.getData && e.dataTransfer.getData('text/plain'));
      if (draggedId && draggedId !== item.id) {
        moveQueueItem(draggedId, item.id, queueState.queue, renderQueue, saveQueue2);
      }
    });
    return { li, text, editBtn, deleteBtn };
  }

  // AI Queue/core/storage.js
  var GLOBAL_CHAT_KEY = '__global__';
  var DEFAULT_ITEM_STATUS = 'queued';
  var DEFAULT_FAILED_STATUS = 'failed';
  function toChatCode(value) {
    if (typeof value !== 'string') return null;
    const normalized = value.trim();
    return normalized ? normalized : null;
  }
  function resolveScope(currentScope = null) {
    if (typeof currentScope === 'string') {
      return {
        chatId: toChatCode(currentScope),
        groupId: null,
      };
    }
    if (!currentScope || typeof currentScope !== 'object') {
      return {
        chatId: null,
        groupId: null,
      };
    }
    const chatId = toChatCode(currentScope.chatId || currentScope.chatCode || null);
    const groupId = toChatCode(currentScope.groupId || null);
    return {
      chatId,
      groupId,
    };
  }
  function resolveScopeKeys(currentScope = null) {
    const scope = resolveScope(currentScope);
    return [...new Set([scope.groupId, scope.chatId].filter(Boolean))];
  }
  function hasItemScope(item) {
    return !!(item?.chatId || item?.chatCode || item?.groupId);
  }
  function applyScopeToQueuedItems(queue, failedQueue, currentScope = null) {
    const scope = resolveScope(currentScope);
    if (!scope.chatId && !scope.groupId) return false;
    let updated = false;
    const applyScope = (item) => {
      if (!item || hasItemScope(item)) return;
      if (scope.chatId) {
        item.chatId = scope.chatId;
        item.chatCode = scope.chatId;
      }
      if (scope.groupId) {
        item.groupId = scope.groupId;
      }
      updated = true;
    };
    (queue || []).forEach(applyScope);
    if (Array.isArray(failedQueue)) {
      failedQueue.forEach(applyScope);
    }
    return updated;
  }
  function toChatKey(value) {
    return toChatCode(value) || GLOBAL_CHAT_KEY;
  }
  function generateId() {
    if (globalThis.crypto?.randomUUID) {
      return globalThis.crypto.randomUUID();
    }
    return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
  }
  function toCreatedAt(value) {
    const timestamp = Number(value);
    return Number.isFinite(timestamp) ? timestamp : Date.now();
  }
  function sanitizeItem(item, defaultStatus = DEFAULT_ITEM_STATUS) {
    if (!item || typeof item.prompt !== 'string') return null;
    const chatId = toChatCode(item.chatId || item.chatCode || null);
    const groupId = toChatCode(item.groupId || null);
    const normalized = {
      id: typeof item.id === 'string' && item.id ? item.id : generateId(),
      prompt: item.prompt,
      attempts: Number.isFinite(Number(item.attempts)) ? Number(item.attempts) : 0,
      status:
        typeof item.status === 'string' && item.status.trim() ? item.status.trim() : defaultStatus,
      createdAt: toCreatedAt(item.createdAt),
    };
    if (chatId) {
      normalized.chatId = chatId;
      normalized.chatCode = chatId;
    }
    if (groupId) {
      normalized.groupId = groupId;
    }
    return normalized;
  }
  function sanitizeItems(items, defaultStatus = DEFAULT_ITEM_STATUS) {
    if (!Array.isArray(items)) return [];
    return items.map((item) => sanitizeItem(item, defaultStatus)).filter(Boolean);
  }
  function emptyChats() {
    return {};
  }
  function sanitizeChatBuckets(chats) {
    const normalized = emptyChats();
    if (!chats || typeof chats !== 'object' || Array.isArray(chats)) {
      return normalized;
    }
    for (const [chatKey, bucket] of Object.entries(chats)) {
      normalized[toChatKey(chatKey)] = {
        items: sanitizeItems(bucket?.items, DEFAULT_ITEM_STATUS),
        failedItems: sanitizeItems(bucket?.failedItems, DEFAULT_FAILED_STATUS),
      };
    }
    return normalized;
  }
  function addLegacyItem(chats, item, defaultStatus, bucketName) {
    const normalizedItem = sanitizeItem(item, defaultStatus);
    if (!normalizedItem) return;
    const chatKey = toChatKey(item?.chatCode);
    if (!chats[chatKey]) {
      chats[chatKey] = { items: [], failedItems: [] };
    }
    chats[chatKey][bucketName].push(normalizedItem);
  }
  function mergeUniqueItems(itemsA, itemsB) {
    const merged = /* @__PURE__ */ new Map();
    [...(itemsA || []), ...(itemsB || [])].forEach((item) => {
      if (!item || typeof item.id !== 'string') return;
      if (!merged.has(item.id)) {
        merged.set(item.id, { ...item });
      }
    });
    return [...merged.values()];
  }
  function buildLegacyData(parsed) {
    const chats = emptyChats();
    if (Array.isArray(parsed?.items)) {
      parsed.items.forEach((item) => addLegacyItem(chats, item, DEFAULT_ITEM_STATUS, 'items'));
    } else if (Array.isArray(parsed?.queue)) {
      parsed.queue.forEach((item) => addLegacyItem(chats, item, DEFAULT_ITEM_STATUS, 'items'));
    }
    if (Array.isArray(parsed?.failedItems)) {
      parsed.failedItems.forEach((item) =>
        addLegacyItem(chats, item, DEFAULT_FAILED_STATUS, 'failedItems')
      );
    } else if (Array.isArray(parsed?.failedQueue)) {
      parsed.failedQueue.forEach((item) =>
        addLegacyItem(chats, item, DEFAULT_FAILED_STATUS, 'failedItems')
      );
    }
    return { chats };
  }
  function normalizeData(parsed) {
    if (!parsed || typeof parsed !== 'object') {
      return { chats: emptyChats() };
    }
    if (parsed.chats && typeof parsed.chats === 'object' && !Array.isArray(parsed.chats)) {
      return {
        chats: sanitizeChatBuckets(parsed.chats),
      };
    }
    return buildLegacyData(parsed);
  }
  function readScopedQueueData(storageKey = 'pq-queue-state') {
    try {
      const stored = localStorage.getItem(storageKey);
      if (!stored) {
        return { chats: emptyChats() };
      }
      const parsed = JSON.parse(stored);
      return normalizeData(parsed);
    } catch (err) {
      error('Failed to read queue storage:', err);
      return { chats: emptyChats() };
    }
  }
  function writeScopedQueueData(storageKey = 'pq-queue-state', data = {}) {
    try {
      const normalized = normalizeData(data);
      localStorage.setItem(storageKey, JSON.stringify(normalized));
    } catch (err) {
      error('Failed to write queue storage:', err);
    }
  }
  function saveQueue(queue, failedQueue, storageKey = 'pq-queue-state', currentScope = null) {
    try {
      const scope = resolveScope(currentScope);
      const scopeKeys = resolveScopeKeys(currentScope);
      const data = readScopedQueueData(storageKey);
      const existingBuckets = scopeKeys.map(
        (scopeKey) => data.chats[scopeKey] || { items: [], failedItems: [] }
      );
      const nextItems = sanitizeItems(queue || [], DEFAULT_ITEM_STATUS).map((item) => ({
        ...item,
        ...(scope.chatId
          ? {
              chatId: scope.chatId,
              chatCode: scope.chatId,
            }
          : {}),
        ...(scope.groupId ? { groupId: scope.groupId } : {}),
      }));
      const nextFailedItems = Array.isArray(failedQueue)
        ? sanitizeItems(failedQueue || [], DEFAULT_FAILED_STATUS).map((item) => ({
            ...item,
            ...(scope.chatId
              ? {
                  chatId: scope.chatId,
                  chatCode: scope.chatId,
                }
              : {}),
            ...(scope.groupId ? { groupId: scope.groupId } : {}),
          }))
        : mergeUniqueItems(
            ...existingBuckets.map((bucket) =>
              sanitizeItems(bucket.failedItems, DEFAULT_FAILED_STATUS)
            )
          );
      const nextBucket = {
        items: nextItems,
        failedItems: nextFailedItems,
      };
      const nextChats = { ...data.chats };
      scopeKeys.forEach((scopeKey) => {
        nextChats[scopeKey] = {
          items: nextItems.map((item) => ({ ...item })),
          failedItems: nextFailedItems.map((item) => ({ ...item })),
        };
      });
      writeScopedQueueData(storageKey, {
        chats: nextChats,
      });
      log('queue saved to storage', {
        storageKey,
        currentChatCode: scopeKeys,
        visibleItems: nextBucket.items.length,
      });
    } catch (err) {
      error('Failed to save queue:', err);
    }
  }
  function loadQueue(queue, failedQueue, storageKey = 'pq-queue-state', currentScope = null) {
    try {
      const scopeKeys = resolveScopeKeys(currentScope);
      const data = readScopedQueueData(storageKey);
      const buckets = scopeKeys.map(
        (scopeKey) => data.chats[scopeKey] || { items: [], failedItems: [] }
      );
      const visibleQueueItems = mergeUniqueItems(...buckets.map((bucket) => bucket.items));
      queue.push(...visibleQueueItems.map((item) => ({ ...item })));
      if (Array.isArray(failedQueue)) {
        const visibleFailedItems = mergeUniqueItems(...buckets.map((bucket) => bucket.failedItems));
        failedQueue.push(...visibleFailedItems.map((item) => ({ ...item })));
      }
      log('queue loaded from storage', {
        storageKey,
        currentChatCode: scopeKeys,
        visibleItems: visibleQueueItems.length,
      });
    } catch (err) {
      error('Failed to load queue:', err);
    }
  }

  // AI Queue/core/panel-controls.js
  var boundPanels = /* @__PURE__ */ new WeakSet();
  function setupPanelControls({
    createItem,
    renderQueue,
    saveQueue: saveQueue2,
    processQueue,
    openChatManager,
  }) {
    const panel = getPanel();
    if (!panel) return;
    if (boundPanels.has(panel)) return;
    const input = panel.querySelector('#pq-input');
    const addBtn = panel.querySelector('#pq-add');
    const manageChatsBtn = panel.querySelector('#pq-manage-chats');
    const startBtn = panel.querySelector('#pq-start');
    const getToolbarButton = () => document.querySelector('#pq-toolbar-button');
    const handleAddClick = () => {
      const text = input.value.trim();
      if (!text) {
        log('Empty prompt, not adding to queue');
        return;
      }
      if (queueState.editingId !== null) {
        const item = queueState.queue.find((item2) => item2.id === queueState.editingId);
        if (!item) {
          log('Editing item not found in queue:', queueState.editingId);
          queueState.queue.push(createItem(text));
        } else {
          item.prompt = text;
        }
        queueState.editingId = null;
        addBtn.textContent = 'Add To Queue';
      } else {
        queueState.queue.push(createItem(text));
      }
      updateToolbarButton(getToolbarButton(), queueState.queue, queueState.running);
      input.value = '';
      renderQueue();
      saveQueue2();
    };
    addBtn.addEventListener('click', handleAddClick);
    if (manageChatsBtn) {
      manageChatsBtn.addEventListener('click', () => {
        openChatManager?.();
      });
    }
    input.addEventListener('keydown', (e) => {
      if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
        e.preventDefault();
        handleAddClick();
      }
    });
    function updateStartStopButtons() {
      if (!startBtn) return;
      startBtn.textContent = queueState.running ? 'Stop Queue' : 'Start Queue';
      startBtn.disabled = false;
    }
    startBtn.addEventListener('click', async () => {
      if (queueState.running) {
        queueState.running = false;
        setStatus(panel, 'Stopped');
        updateStartStopButtons();
        updateToolbarButton(getToolbarButton(), queueState.queue, queueState.running);
        return;
      }
      queueState.running = true;
      updateStartStopButtons();
      updateToolbarButton(getToolbarButton(), queueState.queue, queueState.running);
      try {
        await processQueue();
      } catch (err) {
        error('Queue processor error:', formatError(err));
      } finally {
        queueState.running = false;
        updateStartStopButtons();
        updateToolbarButton(getToolbarButton(), queueState.queue, queueState.running);
      }
    });
    boundPanels.add(panel);
    updateStartStopButtons();
  }

  // AI Queue/core/drag.js
  var dragBoundPanel = null;
  var listenersBound = false;
  var dragging = false;
  var dragStartX = 0;
  var dragStartY = 0;
  var panelStartX = 0;
  var panelStartY = 0;
  function onMouseMove(e) {
    if (!dragging) return;
    const panel = getPanel();
    if (!panel) return;
    const deltaX = e.clientX - dragStartX;
    const deltaY = e.clientY - dragStartY;
    panel.style.left = panelStartX + deltaX + 'px';
    panel.style.top = panelStartY + deltaY + 'px';
    panel.style.right = 'auto';
    panel.style.bottom = 'auto';
  }
  function onMouseUp() {
    if (dragging) {
      dragging = false;
      log('panel drag ended');
    }
  }
  function bindDocumentListeners() {
    if (listenersBound) return;
    document.addEventListener('mousemove', onMouseMove);
    document.addEventListener('mouseup', onMouseUp);
    listenersBound = true;
  }
  function setupPanelDrag() {
    const panel = getPanel();
    if (!panel) return;
    bindDocumentListeners();
    if (dragBoundPanel === panel) return;
    dragBoundPanel = panel;
    panel.addEventListener(
      'mousedown',
      (e) => {
        if (e.button !== 2) return;
        e.preventDefault();
        e.stopPropagation();
        dragging = true;
        dragStartX = e.clientX;
        dragStartY = e.clientY;
        panelStartX = panel.offsetLeft;
        panelStartY = panel.offsetTop;
        log('panel drag started');
      },
      true
    );
    panel.addEventListener('contextmenu', (e) => {
      if (dragging) {
        e.preventDefault();
      }
    });
  }

  // AI Queue/core/dom.js
  function waitForCondition(
    predicate,
    { timeoutMs = 1e4, intervalMs = 100, description = 'condition' } = {}
  ) {
    const startedAt = Date.now();
    return new Promise((resolve, reject) => {
      const check = async () => {
        try {
          const result = await Promise.resolve(predicate());
          if (result) {
            resolve(result);
            return;
          }
        } catch (err) {
          error('waitForCondition check error:', err);
        }
        const elapsed = Date.now() - startedAt;
        if (elapsed > timeoutMs) {
          error(`Timeout waiting for ${description} (${elapsed}ms)`);
          reject(new Error(`Timeout waiting for ${description} (${elapsed}ms`));
          return;
        }
        setTimeout(check, intervalMs);
      };
      check();
    });
  }
  function waitForElement(getter, options = {}) {
    return waitForCondition(() => getter(), options);
  }
  function safeClick(element) {
    if (!isAttached(element) || !isVisible(element)) return false;
    element.focus?.({ preventScroll: true });
    if (typeof element.click === 'function') {
      element.click();
      return true;
    }
    element.dispatchEvent(
      new MouseEvent('mousedown', { bubbles: true, cancelable: true, view: window })
    );
    element.dispatchEvent(
      new MouseEvent('mouseup', { bubbles: true, cancelable: true, view: window })
    );
    element.dispatchEvent(
      new MouseEvent('click', { bubbles: true, cancelable: true, view: window })
    );
    return true;
  }
  function getEditorText(editor) {
    if (!editor) return '';
    if ('value' in editor) return String(editor.value || '');
    return String(editor.textContent || '');
  }
  function isEditableCandidate(element) {
    if (!element) return false;
    if (!(element instanceof HTMLElement)) return false;
    if (!isAttached(element)) return false;
    if (!isVisible(element)) return false;
    const isTextarea = element instanceof HTMLTextAreaElement;
    const isContentEditable =
      element.isContentEditable || element.getAttribute('contenteditable') === 'true';
    if (!isTextarea && !isContentEditable) return false;
    if (element.matches('button, [role="button"], input[type="button"], input[type="submit"]'))
      return false;
    if (element.disabled || element.getAttribute('aria-disabled') === 'true') return false;
    if (element.closest('#pq-panel')) return false;
    return true;
  }
  function scoreEditor(editor) {
    const rect = editor.getBoundingClientRect();
    let score = rect.top;
    if (editor === document.activeElement) score += 1e3;
    if (editor.matches('textarea')) score += 100;
    if (editor.matches('[contenteditable="true"]')) score += 80;
    if ((editor.getAttribute('role') || '').toLowerCase() === 'textbox') score += 60;
    if (editor.closest('form')) score += 30;
    if (editor.closest('[role="form"]')) score += 20;
    if (rect.bottom > window.innerHeight * 0.5) score += 50;
    return score;
  }
  function getComposerEditor() {
    const activeElement = document.activeElement;
    if (isEditableCandidate(activeElement)) {
      if (isActionButtonElement(activeElement)) return null;
      log('editor found', activeElement);
      return activeElement;
    }
    const candidates = [
      ...document.querySelectorAll(
        'textarea:not(#pq-input), [contenteditable="true"][role="textbox"], [contenteditable="true"]'
      ),
    ]
      .filter(isEditableCandidate)
      .sort((left, right) => scoreEditor(right) - scoreEditor(left));
    candidates.forEach((candidate) => log('editor candidate', candidate.tagName, candidate));
    const editor = candidates[0] || null;
    if (editor && editor.matches('button, [role="button"]')) return null;
    if (editor) log('editor found', editor);
    return editor;
  }
  function getComposerHost(editor = getComposerEditor()) {
    if (!editor) return null;
    const host =
      editor.closest(
        'form, [role="form"], [aria-label*="prompt" i], [aria-label*="composer" i], [aria-label*="message" i], [data-testid*="prompt" i], [data-testid*="composer" i]'
      ) ||
      editor.parentElement ||
      null;
    if (!host || host === editor || editor.contains(host) || host.contains?.(editor)) return null;
    if (
      host.isContentEditable ||
      host.matches?.(
        '[contenteditable="true"], textarea, input, button, [role="button"], input[type="button"], input[type="submit"]'
      )
    )
      return null;
    return host;
  }
  function getButtonLabel(button) {
    return [button.getAttribute('aria-label'), button.getAttribute('title'), button.textContent]
      .filter(Boolean)
      .join(' ')
      .replace(/\s+/g, ' ')
      .trim();
  }
  function isActionButtonVisible(button) {
    return isAttached(button) && isVisible(button);
  }
  function getSendButton({ includeDisabled = false } = {}) {
    const host = getComposerHost();
    const selectors = [
      'button[data-testid="send-button"]',
      '[role="button"][data-testid="send-button"]',
      'button[aria-label*="Send" i]',
      'button[title*="Send" i]',
      '[role="button"][aria-label*="Send" i]',
      '[role="button"][title*="Send" i]',
    ];
    const candidates = [];
    for (const selector of selectors) {
      candidates.push(...document.querySelectorAll(selector));
    }
    if (host) {
      candidates.push(...host.querySelectorAll('button, [role="button"]'));
    }
    const button =
      candidates.find((candidate) => {
        if (!candidate || !(candidate instanceof HTMLElement)) return false;
        if (!isActionButtonVisible(candidate)) return false;
        const label = getButtonLabel(candidate);
        const exactSend = candidate.matches('[data-testid="send-button"]');
        const looksLikeSend = /\bsend\b/i.test(label) || /\bsubmit\b/i.test(label);
        if (!exactSend && !looksLikeSend) return false;
        if (!includeDisabled && candidate.disabled) return false;
        return true;
      }) || null;
    if (button) log('send button found', button);
    return button;
  }
  function findStopButton() {
    const selectors = [
      'button[data-testid="stop-button"]',
      '[role="button"][data-testid="stop-button"]',
      'button[aria-label*="Stop" i]',
      'button[title*="Stop" i]',
      '[role="button"][aria-label*="Stop" i]',
      '[role="button"][title*="Stop" i]',
    ];
    for (const selector of selectors) {
      const button = [...document.querySelectorAll(selector)].find(isActionButtonVisible) || null;
      if (button) {
        log('stop button found', button);
        return button;
      }
    }
    return null;
  }
  function hasBusyIndicators() {
    return [
      ...document.querySelectorAll(
        '[aria-busy="true"], [data-loading="true"], [role="progressbar"]'
      ),
    ].some(isActionButtonVisible);
  }

  // AI Queue/core/generation.js
  var lastGenerationLabel = '';
  function getGenerationState() {
    const editor = getComposerEditor();
    const sendButton = getSendButton({ includeDisabled: true });
    const stopButton = findStopButton();
    const hasPrompt = !!getEditorText(editor).trim();
    const busyIndicators = hasBusyIndicators();
    const generating = Boolean(
      stopButton || busyIndicators || (sendButton && sendButton.disabled && hasPrompt)
    );
    const label = JSON.stringify({
      generating,
      hasPrompt,
      busyIndicators,
      sendDisabled: !!(sendButton && sendButton.disabled),
      stopButton: !!stopButton,
    });
    if (label !== lastGenerationLabel) {
      lastGenerationLabel = label;
      log('generation state', {
        generating,
        hasPrompt,
        busyIndicators,
        sendDisabled: !!(sendButton && sendButton.disabled),
        stopButton: !!stopButton,
      });
    }
    return { generating, editor, sendButton, stopButton, busyIndicators, hasPrompt };
  }
  async function waitForIdle({ timeoutMs = 6e4, intervalMs = 200 } = {}) {
    try {
      await waitForCondition(
        async () => {
          const { generating } = getGenerationState();
          return !generating;
        },
        {
          timeoutMs,
          intervalMs,
          description: 'AI to become idle',
        }
      );
      await sleep(300);
    } catch (err) {
      log('waitForIdle timed out:', formatError(err));
      await sleep(300);
    }
  }
  async function waitForGenerationStart({ timeoutMs = 8e3, intervalMs = 100 } = {}) {
    return waitForCondition(() => getGenerationState().generating, {
      timeoutMs,
      intervalMs,
      description: 'Generation to start',
    });
  }
  async function waitForPromptProcessing() {
    try {
      await waitForGenerationStart();
    } catch (err) {
      log('Generation did not start:', formatError(err));
    }
    await waitForIdle();
  }

  // AI Queue/core/keyboard.js
  function isScrollableElement(element) {
    if (!element || !(element instanceof HTMLElement)) return false;
    const style = window.getComputedStyle(element);
    if (!style) return false;
    const overflowY = style.overflowY;
    const overflowX = style.overflowX;
    const canScrollY =
      /(auto|scroll|overlay)/i.test(overflowY) && element.scrollHeight > element.clientHeight;
    const canScrollX =
      /(auto|scroll|overlay)/i.test(overflowX) && element.scrollWidth > element.clientWidth;
    return canScrollY || canScrollX;
  }
  function getScrollTargets(element) {
    const targets = [];
    const seen = /* @__PURE__ */ new Set();
    const addTarget = (target) => {
      if (!target || seen.has(target)) return;
      seen.add(target);
      targets.push(target);
    };
    addTarget(window);
    const scrollingElement = document.scrollingElement || document.documentElement;
    if (scrollingElement) {
      addTarget(scrollingElement);
    }
    if (document.body) {
      addTarget(document.body);
    }
    let current = element?.parentElement || null;
    while (current) {
      if (isScrollableElement(current)) {
        addTarget(current);
      }
      current = current.parentElement;
    }
    const root = document.body || document.documentElement;
    if (root) {
      for (const candidate of root.querySelectorAll('*')) {
        if (isScrollableElement(candidate)) {
          addTarget(candidate);
        }
      }
    }
    return targets;
  }
  function getScrollPosition(target) {
    if (target === window) {
      return { x: window.scrollX, y: window.scrollY };
    }
    return {
      x: target.scrollLeft,
      y: target.scrollTop,
    };
  }
  function restoreScrollPosition(target, position) {
    if (target === window) {
      window.scrollTo(position.x, position.y);
      return;
    }
    target.scrollLeft = position.x;
    target.scrollTop = position.y;
  }
  function createScrollApiSuppressor() {
    const restorers = [];
    const patch = (target, key, replacement) => {
      if (!target || typeof target[key] !== 'function') return;
      const original = target[key];
      target[key] = replacement;
      restorers.push(() => {
        target[key] = original;
      });
    };
    patch(Element.prototype, 'scrollIntoView', function () {});
    patch(window, 'scrollTo', function () {});
    patch(window, 'scrollBy', function () {});
    patch(Element.prototype, 'scroll', function () {});
    patch(Element.prototype, 'scrollTo', function () {});
    patch(Element.prototype, 'scrollBy', function () {});
    return () => {
      for (let index = restorers.length - 1; index >= 0; index -= 1) {
        restorers[index]();
      }
    };
  }
  async function withPreservedViewport(action, targets = []) {
    const scrollTargets = targets.length > 0 ? targets : [window];
    const scrollPositions = new Map(
      scrollTargets.map((target) => [target, getScrollPosition(target)])
    );
    const originalAnchors = /* @__PURE__ */ new Map();
    let restoring = false;
    const restoreScrollApis = createScrollApiSuppressor();
    for (const target of scrollTargets) {
      if (target instanceof HTMLElement) {
        originalAnchors.set(target, target.style.overflowAnchor);
        target.style.overflowAnchor = 'none';
      }
    }
    const restoreScroll = () => {
      if (restoring || userInteracting) return;
      restoring = true;
      for (const target of scrollTargets) {
        const position = scrollPositions.get(target);
        if (!position) continue;
        const currentPosition = getScrollPosition(target);
        if (currentPosition.x === position.x && currentPosition.y === position.y) continue;
        restoreScrollPosition(target, position);
      }
      restoring = false;
    };
    const keepViewportStable = () => {
      restoreScroll();
    };
    let userInteracting = false;
    let userInteractTimer = null;
    const onUserInteraction = (ev) => {
      userInteracting = true;
      if (userInteractTimer) clearTimeout(userInteractTimer);
      userInteractTimer = setTimeout(() => {
        userInteracting = false;
        userInteractTimer = null;
        try {
          for (const t of scrollTargets) {
            scrollPositions.set(t, getScrollPosition(t));
          }
        } catch (err) {
          error('Error updating scroll positions after user interaction:', formatError(err));
        }
      }, 300);
    };
    window.addEventListener('wheel', onUserInteraction, { passive: true, capture: true });
    window.addEventListener('touchstart', onUserInteraction, { passive: true, capture: true });
    window.addEventListener('touchmove', onUserInteraction, { passive: true, capture: true });
    window.addEventListener('pointerdown', onUserInteraction, { passive: true, capture: true });
    window.addEventListener('keydown', onUserInteraction, true);
    window.addEventListener('scroll', keepViewportStable, true);
    window.addEventListener('resize', keepViewportStable);
    const restoreTimer = window.setInterval(restoreScroll, 50);
    try {
      return await action();
    } finally {
      window.clearInterval(restoreTimer);
      window.removeEventListener('scroll', keepViewportStable, true);
      window.removeEventListener('resize', keepViewportStable);
      window.removeEventListener('wheel', onUserInteraction, true);
      window.removeEventListener('touchstart', onUserInteraction, true);
      window.removeEventListener('touchmove', onUserInteraction, true);
      window.removeEventListener('pointerdown', onUserInteraction, true);
      window.removeEventListener('keydown', onUserInteraction, true);
      if (userInteractTimer) {
        clearTimeout(userInteractTimer);
        try {
          for (const t of scrollTargets) {
            scrollPositions.set(t, getScrollPosition(t));
          }
        } catch (err) {
          error('Error updating scroll positions during cleanup:', formatError(err));
        }
        userInteractTimer = null;
      }
      restoreScrollApis();
      for (const target of scrollTargets) {
        if (target instanceof HTMLElement && originalAnchors.has(target)) {
          target.style.overflowAnchor = originalAnchors.get(target);
        }
      }
      restoreScroll();
    }
  }
  function dispatchEnterKey(target) {
    const eventInit = {
      bubbles: true,
      cancelable: true,
      key: 'Enter',
      code: 'Enter',
      keyCode: 13,
      which: 13,
    };
    for (const type of ['keydown', 'keypress', 'keyup']) {
      target.dispatchEvent(new KeyboardEvent(type, eventInit));
    }
  }
  function setEditorValue(editor, prompt) {
    if (!editor) throwError('Editor not found');
    if (!isAttached(editor)) throwError('Editor is detached');
    editor.focus?.({ preventScroll: true });
    if ('value' in editor) {
      const setter =
        Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set ||
        Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;
      if (setter) {
        setter.call(editor, prompt);
      } else {
        editor.value = prompt;
      }
      if ('selectionStart' in editor) {
        editor.selectionStart = editor.selectionEnd = editor.value.length;
      }
      editor.dispatchEvent(
        new InputEvent('beforeinput', {
          bubbles: true,
          cancelable: true,
          inputType: 'insertText',
          data: prompt,
        })
      );
      editor.dispatchEvent(
        new InputEvent('input', {
          bubbles: true,
          cancelable: true,
          inputType: 'insertText',
          data: prompt,
        })
      );
      editor.dispatchEvent(new Event('change', { bubbles: true }));
      return;
    }
    if (editor.isContentEditable) {
      editor.focus?.({ preventScroll: true });
      const selection = window.getSelection();
      if (selection) {
        const range = document.createRange();
        range.selectNodeContents(editor);
        selection.removeAllRanges();
        selection.addRange(range);
        document.execCommand('delete', false);
      }
      editor.textContent = prompt;
      editor.dispatchEvent(
        new InputEvent('input', {
          bubbles: true,
          cancelable: true,
          inputType: 'insertText',
          data: prompt,
        })
      );
      editor.dispatchEvent(new Event('change', { bubbles: true }));
      return;
    }
    throwError('Unsupported editor type');
  }
  async function sendPrompt(prompt) {
    const editor = await waitForElement(() => getComposerEditor(), {
      timeoutMs: 15e3,
      intervalMs: 200,
      description: 'Composer editor',
    });
    const scrollTargets = getScrollTargets(editor);
    return withPreservedViewport(async () => {
      setEditorValue(editor, prompt);
      try {
        await waitForCondition(
          () => {
            const btn = getSendButton();
            return btn && !btn.disabled;
          },
          {
            timeoutMs: 5e3,
            intervalMs: 100,
            description: 'send button to enable',
          }
        );
        const sendButton = getSendButton();
        if (sendButton) {
          safeClick(sendButton);
          await new Promise((resolve) => setTimeout(resolve, 100));
          await waitForPromptProcessing();
          return;
        }
      } catch (err) {
        error('send button unavailable, falling back to Enter', formatError(err));
      }
      dispatchEnterKey(editor);
      if (editor.form && typeof editor.form.requestSubmit === 'function') {
        editor.form.requestSubmit();
      }
      await waitForPromptProcessing();
    }, scrollTargets);
  }

  // AI Queue/styles/chat-manager.css
  var chat_manager_default =
    ':root {\r\n  color-scheme: dark;\r\n  --pq-manager-bg: #0b1220;\r\n  --pq-manager-panel: rgba(17, 24, 39, 0.96);\r\n  --pq-manager-card: rgba(31, 41, 55, 0.96);\r\n  --pq-manager-text: #f3f4f6;\r\n  --pq-manager-muted: #9ca3af;\r\n  --pq-manager-border: #374151;\r\n  --pq-manager-accent: #60a5fa;\r\n  --pq-manager-accent-strong: #22c55e;\r\n}\r\n\r\n#pq-chat-manager-panel {\r\n  position: fixed;\r\n  top: 6vh;\r\n  left: 50%;\r\n  transform: translateX(-50%);\r\n  width: min(1100px, calc(100vw - 32px));\r\n  height: min(760px, calc(100vh - 32px));\r\n  z-index: 2147483647;\r\n  display: flex;\r\n  flex-direction: column;\r\n  background: radial-gradient(circle at top right, rgba(31, 41, 55, 0.95), rgba(11, 18, 32, 0.98) 60%);\r\n  color: var(--pq-manager-text);\r\n  border: 1px solid var(--pq-manager-border);\r\n  border-radius: 16px;\r\n  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);\r\n  overflow: hidden;\r\n}\r\n\r\n.pq-manager-shell {\r\n  display: flex;\r\n  flex-direction: column;\r\n  height: 100%;\r\n  min-height: 0;\r\n}\r\n\r\n.pq-manager-header {\r\n  display: flex;\r\n  align-items: flex-start;\r\n  justify-content: space-between;\r\n  gap: 16px;\r\n  padding: 16px 18px 12px;\r\n  border-bottom: 1px solid var(--pq-manager-border);\r\n  background: linear-gradient(180deg, rgba(17, 24, 39, 0.95), rgba(17, 24, 39, 0.82));\r\n}\r\n\r\n.pq-manager-title {\r\n  font-size: 18px;\r\n  font-weight: 700;\r\n  line-height: 1.2;\r\n  margin: 0;\r\n}\r\n\r\n.pq-manager-subtitle {\r\n  margin-top: 4px;\r\n  color: var(--pq-manager-muted);\r\n  font-size: 13px;\r\n  line-height: 1.4;\r\n  max-width: 72ch;\r\n}\r\n\r\n.pq-manager-actions {\r\n  display: flex;\r\n  gap: 8px;\r\n  flex-shrink: 0;\r\n}\r\n\r\n.pq-manager-actions button {\r\n  appearance: none;\r\n  border: 1px solid var(--pq-manager-border);\r\n  background: rgba(31, 41, 55, 0.95);\r\n  color: var(--pq-manager-text);\r\n  border-radius: 999px;\r\n  padding: 8px 12px;\r\n  font: inherit;\r\n  cursor: pointer;\r\n}\r\n\r\n.pq-manager-actions button:hover {\r\n  border-color: var(--pq-manager-accent);\r\n}\r\n\r\n.pq-manager-body {\r\n  display: flex;\r\n  flex-direction: column;\r\n  min-height: 0;\r\n  padding: 16px 18px 18px;\r\n  gap: 12px;\r\n  overflow: hidden;\r\n}\r\n\r\n.pq-manager-grid {\r\n  display: grid;\r\n  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\r\n  gap: 12px;\r\n  align-items: start;\r\n  flex: 1;\r\n  min-height: 0;\r\n  overflow: auto;\r\n  padding-right: 4px;\r\n}\r\n\r\n.chat-card {\r\n  background: linear-gradient(180deg, var(--pq-manager-panel), var(--pq-manager-card));\r\n  border: 1px solid var(--pq-manager-border);\r\n  border-radius: 12px;\r\n  overflow: hidden;\r\n  min-height: 140px;\r\n}\r\n\r\n.chat-title {\r\n  padding: 10px 12px;\r\n  border-bottom: 1px solid var(--pq-manager-border);\r\n  font-size: 12px;\r\n  letter-spacing: 0.2px;\r\n  text-transform: uppercase;\r\n  color: #d1d5db;\r\n  display: flex;\r\n  justify-content: space-between;\r\n  gap: 8px;\r\n}\r\n\r\n.chat-title .chat-controls {\r\n  display: inline-flex;\r\n  gap: 8px;\r\n  align-items: center;\r\n}\r\n\r\n.chat-delete {\r\n  appearance: none;\r\n  border: 1px solid transparent;\r\n  background: transparent;\r\n  color: var(--pq-manager-muted);\r\n  border-radius: 8px;\r\n  padding: 4px 8px;\r\n  font-size: 12px;\r\n  cursor: pointer;\r\n}\r\n\r\n.chat-delete:hover {\r\n  color: var(--pq-manager-accent);\r\n  border-color: rgba(96, 165, 250, 0.12);\r\n  background: rgba(96, 165, 250, 0.03);\r\n}\r\n\r\n.chat-list {\r\n  list-style: none;\r\n  margin: 0;\r\n  padding: 8px;\r\n  min-height: 90px;\r\n}\r\n\r\n.chat-item {\r\n  background: rgba(17, 24, 39, 0.7);\r\n  border: 1px solid #334155;\r\n  border-radius: 8px;\r\n  padding: 8px;\r\n  margin-bottom: 8px;\r\n  cursor: grab;\r\n  user-select: none;\r\n  font-size: 13px;\r\n  line-height: 1.35;\r\n  word-break: break-word;\r\n}\r\n\r\n.chat-item.dragging {\r\n  opacity: 0.5;\r\n}\r\n\r\n.chat-list.drag-over,\r\n.chat-item.drag-over {\r\n  outline: 2px dashed var(--pq-manager-accent);\r\n  outline-offset: 2px;\r\n}\r\n\r\n.empty {\r\n  color: var(--pq-manager-muted);\r\n  font-size: 12px;\r\n  padding: 8px;\r\n  border: 1px dashed var(--pq-manager-border);\r\n  border-radius: 8px;\r\n  text-align: center;\r\n}\r\n\r\n.pq-manager-footer {\r\n  color: var(--pq-manager-muted);\r\n  font-size: 12px;\r\n  border-top: 1px solid var(--pq-manager-border);\r\n  padding-top: 12px;\r\n}';

  // AI Queue/core/chat-manager.js
  var GLOBAL_CHAT_KEY2 = '__global__';
  var MANAGER_PANEL_ID = 'pq-chat-manager-panel';
  var MANAGER_GRID_ID = 'pq-chat-manager-grid';
  var MANAGER_BODY_ID = 'pq-chat-manager-body';
  var activeManagers = /* @__PURE__ */ new Map();
  function toChatCode2(chatKey) {
    return chatKey === GLOBAL_CHAT_KEY2 ? null : chatKey;
  }
  function chatLabel(chatKey) {
    if (chatKey === GLOBAL_CHAT_KEY2) {
      return 'Global (all chats)';
    }
    return chatKey;
  }
  function cloneItem(item) {
    return { ...item };
  }
  function cloneItems(items) {
    return Array.isArray(items) ? items.map((item) => cloneItem(item)) : [];
  }
  function groupItems(chats) {
    const grouped = { [GLOBAL_CHAT_KEY2]: [] };
    Object.entries(chats || {}).forEach(([chatKey, bucket]) => {
      if (!grouped[chatKey]) {
        grouped[chatKey] = [];
      }
      grouped[chatKey].push(...cloneItems(bucket?.items));
    });
    return grouped;
  }
  function orderedKeys(groups) {
    const keys = Object.keys(groups).filter((key) => groups[key]?.length > 0);
    const nonGlobal = keys
      .filter((key) => key !== GLOBAL_CHAT_KEY2)
      .sort((a, b) => a.localeCompare(b));
    if (groups[GLOBAL_CHAT_KEY2]?.length > 0) {
      return [GLOBAL_CHAT_KEY2, ...nonGlobal];
    }
    return nonGlobal;
  }
  function flattenGroups(groups, existingChats = {}) {
    const nextChats = {};
    orderedKeys(groups).forEach((key) => {
      nextChats[key] = {
        items: cloneItems(groups[key]),
        failedItems: cloneItems(existingChats[key]?.failedItems),
      };
    });
    Object.entries(existingChats).forEach(([chatKey, bucket]) => {
      if (nextChats[chatKey]) return;
      if (Array.isArray(bucket?.failedItems) && bucket.failedItems.length > 0) {
        nextChats[chatKey] = {
          items: [],
          failedItems: cloneItems(bucket.failedItems),
        };
      }
    });
    return nextChats;
  }
  function findItemIndex(groups, chatKey, itemId) {
    const list = groups[chatKey] || [];
    return list.findIndex((item) => item.id === itemId);
  }
  function ensureManagerStyles(doc) {
    if (doc.querySelector('#pq-chat-manager-styles')) return;
    const style = doc.createElement('style');
    style.id = 'pq-chat-manager-styles';
    style.textContent = chat_manager_default;
    doc.head.appendChild(style);
  }
  function ensureManagerShell(doc, title, mountTarget) {
    ensureManagerStyles(doc);
    let panel = doc.getElementById(MANAGER_PANEL_ID);
    if (!panel) {
      panel = doc.createElement('section');
      panel.id = MANAGER_PANEL_ID;
      const shell = doc.createElement('div');
      shell.className = 'pq-manager-shell';
      const header = doc.createElement('div');
      header.className = 'pq-manager-header';
      const titleWrap = doc.createElement('div');
      const heading = doc.createElement('div');
      heading.className = 'pq-manager-title';
      const subtitle = doc.createElement('div');
      subtitle.className = 'pq-manager-subtitle';
      titleWrap.appendChild(heading);
      titleWrap.appendChild(subtitle);
      const actions = doc.createElement('div');
      actions.className = 'pq-manager-actions';
      const refreshButton = doc.createElement('button');
      refreshButton.type = 'button';
      refreshButton.id = 'pq-chat-manager-refresh';
      refreshButton.textContent = 'Refresh';
      const closeButton = doc.createElement('button');
      closeButton.type = 'button';
      closeButton.id = 'pq-chat-manager-close';
      closeButton.textContent = 'Close';
      actions.appendChild(refreshButton);
      actions.appendChild(closeButton);
      header.appendChild(titleWrap);
      header.appendChild(actions);
      const body = doc.createElement('div');
      body.className = 'pq-manager-body';
      body.id = MANAGER_BODY_ID;
      const grid = doc.createElement('div');
      grid.className = 'pq-manager-grid';
      grid.id = MANAGER_GRID_ID;
      const footer = doc.createElement('div');
      footer.className = 'pq-manager-footer';
      footer.textContent =
        'Drag prompts between chats. Changes are saved immediately to localStorage.';
      body.appendChild(grid);
      body.appendChild(footer);
      shell.appendChild(header);
      shell.appendChild(body);
      panel.appendChild(shell);
      closeButton.addEventListener('click', () => {
        panel.hidden = true;
        panel.style.display = 'none';
      });
    }
    const titleNode = panel.querySelector('.pq-manager-title');
    const subtitleNode = panel.querySelector('.pq-manager-subtitle');
    if (titleNode) {
      titleNode.textContent = title;
    }
    if (subtitleNode) {
      subtitleNode.textContent =
        'Reorder prompts within a chat or move them into another chat card. This panel stays inside the page instead of opening a popup.';
    }
    const root = mountTarget || doc.documentElement || doc.body;
    if (root && !root.contains(panel)) {
      root.appendChild(panel);
    }
    panel.hidden = false;
    panel.style.display = 'flex';
    return panel;
  }
  function moveByDrop(state, fromChatKey, itemId, toChatKey2, toIndex) {
    const fromList = state.groups[fromChatKey] || [];
    const fromIndex = findItemIndex(state.groups, fromChatKey, itemId);
    if (fromIndex === -1) {
      return false;
    }
    const [movedItem] = fromList.splice(fromIndex, 1);
    if (!state.groups[toChatKey2]) {
      state.groups[toChatKey2] = [];
    }
    const targetList = state.groups[toChatKey2];
    let normalizedIndex = Number.isInteger(toIndex) ? toIndex : targetList.length;
    if (normalizedIndex < 0) normalizedIndex = 0;
    if (normalizedIndex > targetList.length) normalizedIndex = targetList.length;
    if (fromChatKey === toChatKey2 && normalizedIndex > fromIndex) {
      normalizedIndex -= 1;
    }
    movedItem.chatCode = toChatCode2(toChatKey2) || void 0;
    targetList.splice(normalizedIndex, 0, movedItem);
    if (fromChatKey !== GLOBAL_CHAT_KEY2 && (state.groups[fromChatKey] || []).length === 0) {
      delete state.groups[fromChatKey];
    }
    return true;
  }
  function persistState(storageKey, state) {
    state.data.chats = flattenGroups(state.groups, state.data.chats);
    writeScopedQueueData(storageKey, state.data);
  }
  function renderCards(grid, storageKey, state, rerender) {
    if (!grid) return;
    grid.replaceChildren();
    const keys = orderedKeys(state.groups);
    if (keys.length === 0) {
      const doc2 = grid.ownerDocument;
      const empty = doc2.createElement('div');
      empty.className = 'empty';
      empty.textContent = 'No prompts found in storage.';
      grid.appendChild(empty);
      return;
    }
    const doc = grid.ownerDocument;
    keys.forEach((chatKey) => {
      const card = doc.createElement('section');
      card.className = 'chat-card';
      const title = doc.createElement('div');
      title.className = 'chat-title';
      const label = doc.createElement('span');
      label.textContent = chatLabel(chatKey);
      const count = doc.createElement('span');
      count.textContent = String(state.groups[chatKey].length);
      const controls = doc.createElement('span');
      controls.className = 'chat-controls';
      const deleteButton = doc.createElement('button');
      deleteButton.type = 'button';
      deleteButton.className = 'chat-delete';
      deleteButton.textContent = 'Delete';
      deleteButton.title = 'Delete all prompts in this chat';
      deleteButton.addEventListener('click', () => {
        const chatName = chatLabel(chatKey);
        if (
          !doc.defaultView?.confirm(`Delete all prompts in "${chatName}"? This cannot be undone.`)
        )
          return;
        if (state.groups[chatKey]) {
          delete state.groups[chatKey];
        }
        persistState(storageKey, state);
        rerender();
      });
      controls.appendChild(count);
      controls.appendChild(deleteButton);
      title.appendChild(label);
      title.appendChild(controls);
      const list = doc.createElement('ul');
      list.className = 'chat-list';
      list.dataset.chatKey = chatKey;
      const items = state.groups[chatKey];
      if (!items || items.length === 0) {
        const empty = doc.createElement('div');
        empty.className = 'empty';
        empty.textContent = 'Drop a prompt here.';
        list.appendChild(empty);
      } else {
        items.forEach((item, index) => {
          const entry = doc.createElement('li');
          entry.className = 'chat-item';
          entry.draggable = true;
          entry.dataset.chatKey = chatKey;
          entry.dataset.itemId = item.id;
          entry.dataset.index = String(index);
          entry.textContent = item.prompt;
          entry.addEventListener('dragstart', (event) => {
            state.drag = {
              itemId: item.id,
              fromChatKey: chatKey,
            };
            event.dataTransfer.effectAllowed = 'move';
            event.dataTransfer.setData('text/plain', item.id);
            entry.classList.add('dragging');
          });
          entry.addEventListener('dragend', () => {
            state.drag = null;
            entry.classList.remove('dragging');
            doc.querySelectorAll(`#${MANAGER_PANEL_ID} .drag-over`).forEach((element) => {
              element.classList.remove('drag-over');
            });
          });
          entry.addEventListener('dragover', (event) => {
            event.preventDefault();
            entry.classList.add('drag-over');
          });
          entry.addEventListener('dragleave', () => {
            entry.classList.remove('drag-over');
          });
          entry.addEventListener('drop', (event) => {
            event.preventDefault();
            entry.classList.remove('drag-over');
            if (!state.drag) return;
            const moved = moveByDrop(
              state,
              state.drag.fromChatKey,
              state.drag.itemId,
              chatKey,
              Number(entry.dataset.index)
            );
            if (!moved) return;
            persistState(storageKey, state);
            rerender();
          });
          list.appendChild(entry);
        });
      }
      list.addEventListener('dragover', (event) => {
        event.preventDefault();
        list.classList.add('drag-over');
      });
      list.addEventListener('dragleave', () => {
        list.classList.remove('drag-over');
      });
      list.addEventListener('drop', (event) => {
        event.preventDefault();
        list.classList.remove('drag-over');
        if (!state.drag) return;
        const moved = moveByDrop(
          state,
          state.drag.fromChatKey,
          state.drag.itemId,
          chatKey,
          state.groups[chatKey]?.length
        );
        if (!moved) return;
        persistState(storageKey, state);
        rerender();
      });
      card.appendChild(title);
      card.appendChild(list);
      grid.appendChild(card);
    });
  }
  function openChatManagerWindow(storageKey, title = 'Prompt Queue Chat Manager', mountTarget) {
    const data = readScopedQueueData(storageKey);
    const state = {
      data,
      groups: groupItems(data.chats),
      drag: null,
    };
    const doc = mountTarget?.ownerDocument || document;
    const panel = ensureManagerShell(doc, title, mountTarget);
    panel.dataset.storageKey = storageKey;
    const rerender = () => {
      const grid = panel.querySelector(`#${MANAGER_GRID_ID}`);
      renderCards(grid, storageKey, state, rerender);
    };
    rerender();
    const refreshButton = panel.querySelector('#pq-chat-manager-refresh');
    if (refreshButton) {
      refreshButton.onclick = () => {
        const refreshedData = readScopedQueueData(storageKey);
        state.data = refreshedData;
        state.groups = groupItems(refreshedData.chats);
        state.drag = null;
        rerender();
      };
    }
    panel.scrollIntoView?.({ block: 'start', inline: 'nearest', behavior: 'smooth' });
    log('chat manager opened in-page', { storageKey });
    activeManagers.set(storageKey, {
      panel,
      state,
      title,
      rerender,
    });
  }
  function refreshChatManager(storageKey) {
    const manager = activeManagers.get(storageKey);
    if (!manager || !manager.panel || manager.panel.hidden) return false;
    const refreshedData = readScopedQueueData(storageKey);
    manager.state.data = refreshedData;
    manager.state.groups = groupItems(refreshedData.chats);
    manager.state.drag = null;
    const grid = manager.panel.querySelector(`#${MANAGER_GRID_ID}`);
    renderCards(grid, storageKey, manager.state, manager.rerender);
    return true;
  }

  // AI Queue/core/bootstrap.js
  function bootstrapQueueApp(provider) {
    globalThis.aiQueue = queueState;
    log('AI Queue running', true);
    const storageKey = provider.storageKey;
    const syncFromStorage = () => {
      resetQueueState({ includeFailedQueue: !!provider.includeFailedQueue });
      provider.loadQueue?.();
      provider.renderQueue?.();
      provider.ensureToolbarButton?.();
      if (storageKey) {
        refreshChatManager(storageKey);
      }
    };
    const refreshForCurrentUrl = (previousUrl = location.href, currentUrl = location.href) => {
      const getScope = provider.getCurrentScope;
      const previousScope = typeof getScope === 'function' ? getScope(previousUrl) : null;
      const currentScope = typeof getScope === 'function' ? getScope(currentUrl) : null;
      if (
        queueState.running &&
        queueState.awaitingChatScopeSync &&
        !previousScope &&
        currentScope
      ) {
        const updated = applyScopeToQueuedItems(
          queueState.queue,
          queueState.failedQueue,
          currentScope
        );
        if (updated) {
          provider.saveQueue?.();
          provider.renderQueue?.();
          provider.ensureToolbarButton?.();
          if (storageKey) {
            refreshChatManager(storageKey);
          }
        }
        queueState.awaitingChatScopeSync = false;
        return;
      }
      if (queueState.running) {
        queueState.running = false;
      }
      syncFromStorage();
    };
    syncFromStorage();
    provider.createPanel();
    provider.setupPanelControls?.({
      createItem: provider.createItem,
      renderQueue: provider.renderQueue,
      saveQueue: provider.saveQueue,
      processQueue: provider.processQueue,
      openChatManager: provider.openChatManager,
    });
    provider.setupPanelDrag?.();
    provider.renderQueue?.();
    provider.ensureToolbarButton?.();
    if (storageKey) {
      window.addEventListener('storage', (event) => {
        if (event.storageArea !== localStorage) return;
        if (event.key !== storageKey) return;
        syncFromStorage();
      });
    }
    startDomObserver(
      provider.createPanel,
      () =>
        provider.setupPanelControls?.({
          createItem: provider.createItem,
          renderQueue: provider.renderQueue,
          saveQueue: provider.saveQueue,
          processQueue: provider.processQueue,
          openChatManager: provider.openChatManager,
        }),
      provider.setupPanelDrag,
      provider.ensureToolbarButton,
      provider.isOwnMutation
    );
    startUrlWatcher(
      provider.createPanel,
      () =>
        provider.setupPanelControls?.({
          createItem: provider.createItem,
          renderQueue: provider.renderQueue,
          saveQueue: provider.saveQueue,
          processQueue: provider.processQueue,
          openChatManager: provider.openChatManager,
        }),
      provider.setupPanelDrag,
      provider.ensureToolbarButton,
      refreshForCurrentUrl
    );
  }

  // AI Queue/styles/selection-menu.css
  var selection_menu_default =
    '#pq-selection-menu {\r\n  position: fixed;\r\n  z-index: 2147483647;\r\n  display: none;\r\n  min-width: 180px;\r\n  padding: 6px;\r\n  border: 1px solid #444;\r\n  border-radius: 12px;\r\n  background: #202123;\r\n  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.45);\r\n}\r\n\r\n#pq-selection-menu button {\r\n  appearance: none;\r\n  display: block;\r\n  width: 100%;\r\n  padding: 8px 12px;\r\n  border: 1px solid #555;\r\n  border-radius: 10px;\r\n  background: #2a2a2a;\r\n  color: #fff;\r\n  cursor: pointer;\r\n  font: inherit;\r\n  text-align: left;\r\n}\r\n\r\n#pq-selection-menu button:hover {\r\n  background: #343434;\r\n}';

  // AI Queue/core/selection-menu.js
  var SELECTION_MENU_ID = 'pq-selection-menu';
  var installed = false;
  function getSelectedPageText() {
    const selection = window.getSelection?.();
    if (!selection || selection.isCollapsed) return '';
    return selection.toString().trim();
  }
  function hideSelectionMenu() {
    const menu = document.querySelector(`#${SELECTION_MENU_ID}`);
    if (!menu) return;
    menu.hidden = true;
    menu.style.display = 'none';
  }
  function ensureSelectionMenu(onAddSelection) {
    let menu = document.querySelector(`#${SELECTION_MENU_ID}`);
    if (menu) return menu;
    if (!document.querySelector('#pq-selection-menu-styles')) {
      const style = document.createElement('style');
      style.id = 'pq-selection-menu-styles';
      style.textContent = selection_menu_default;
      document.head.appendChild(style);
    }
    menu = document.createElement('div');
    menu.id = SELECTION_MENU_ID;
    menu.hidden = true;
    const button = document.createElement('button');
    button.type = 'button';
    button.textContent = 'Add to Prompt Queue';
    button.addEventListener('click', () => {
      const prompt = menu.dataset.prompt || '';
      if (prompt) {
        onAddSelection(prompt);
      }
      hideSelectionMenu();
    });
    menu.appendChild(button);
    document.body.appendChild(menu);
    return menu;
  }
  function showSelectionMenu(selectionText, x, y, onAddSelection) {
    const menu = ensureSelectionMenu(onAddSelection);
    menu.dataset.prompt = selectionText;
    const margin = 12;
    const left = Math.min(x + margin, window.innerWidth - 200);
    const top = Math.min(y + margin, window.innerHeight - 64);
    menu.style.left = `${Math.max(8, left)}px`;
    menu.style.top = `${Math.max(8, top)}px`;
    menu.hidden = false;
    menu.style.display = 'block';
  }
  function addSelectionToQueue(createItem, renderQueue, saveQueue2, updateToolbarButton2) {
    return (selectionText) => {
      const prompt = selectionText.trim();
      if (!prompt) return;
      queueState.queue.push(createItem(prompt));
      updateToolbarButton2(
        document.querySelector('#pq-toolbar-button'),
        queueState.queue,
        queueState.running
      );
      renderQueue?.();
      saveQueue2();
    };
  }
  function installSelectionPromptMenu({
    createItem,
    renderQueue,
    saveQueue: saveQueue2,
    updateToolbarButton: updateToolbarButton2,
  }) {
    if (installed) return;
    installed = true;
    const onAddSelection = addSelectionToQueue(
      createItem,
      renderQueue,
      saveQueue2,
      updateToolbarButton2
    );
    document.addEventListener(
      'contextmenu',
      (event) => {
        const target = event.target;
        if (!(target instanceof Element)) return;
        if (target.closest('#pq-panel') || target.closest(`#${SELECTION_MENU_ID}`)) {
          hideSelectionMenu();
          return;
        }
        const selectionText = getSelectedPageText();
        if (!selectionText) {
          hideSelectionMenu();
          return;
        }
        showSelectionMenu(selectionText, event.clientX, event.clientY, onAddSelection);
      },
      true
    );
    document.addEventListener('click', (event) => {
      const menu = document.querySelector(`#${SELECTION_MENU_ID}`);
      if (!menu) return;
      if (menu.contains(event.target)) return;
      hideSelectionMenu();
    });
    window.addEventListener('blur', hideSelectionMenu);
    window.addEventListener('scroll', hideSelectionMenu, true);
    window.addEventListener('resize', hideSelectionMenu);
  }

  // AI Queue/providers/gemini.js
  var STORAGE_KEY = 'pq-gemini-queue';
  var DOMAINS = ['gemini.google.com'];
  function normalizeCode(value) {
    if (typeof value !== 'string') return null;
    const trimmed = value.trim();
    return trimmed ? trimmed : null;
  }
  function getCurrentGeminiChatCode(url = globalThis.location?.href || '') {
    try {
      const parsedUrl = new URL(url, globalThis.location?.origin || 'https://example.com');
      if (!DOMAINS.includes(parsedUrl.hostname.toLowerCase())) {
        return null;
      }
      const segments = parsedUrl.pathname.split('/').filter(Boolean);
      if (segments[0] !== 'app') return null;
      return normalizeCode(segments[1]);
    } catch {
      return null;
    }
  }
  function queryPanel() {
    return document.querySelector('#pq-panel');
  }
  function queryInput() {
    return queryPanel()?.querySelector('#pq-input');
  }
  function queryAddButton() {
    return queryPanel()?.querySelector('#pq-add');
  }
  function createGeminiPanel() {
    return createBasePanel('Gemini Prompt Queue', true);
  }
  function renderGeminiQueue() {
    const panel = queryPanel();
    if (!panel) return;
    const list = panel.querySelector('#pq-list');
    const failedList = panel.querySelector('#pq-failed-list');
    const failedTitle = panel.querySelector('#pq-failed-title');
    if (!list) return;
    while (list.firstChild) {
      list.removeChild(list.firstChild);
    }
    queueState.queue.forEach((item) => {
      const { li, text, editBtn, deleteBtn } = createQueueItemElement(item, {
        renderQueue: renderGeminiQueue,
        saveQueue: saveGeminiQueue,
      });
      if (queueState.editingId == item.id) {
        li.querySelector('div').style.backgroundColor = '#333';
        li.querySelector('div').style.padding = '4px';
        li.querySelector('div').style.borderRadius = '4px';
      }
      text.addEventListener('dblclick', () => {
        editQueueItem(item.id, queueState.queue, (id, prompt) => {
          queueState.editingId = id;
          const input = queryInput();
          const addButton = queryAddButton();
          if (input && addButton) {
            input.value = prompt;
            addButton.textContent = 'Save Changes';
            input.focus();
            input.selectionStart = input.selectionEnd = input.value.length;
          }
          editBtn.style.display = 'inline-block';
          deleteBtn.style.display = 'inline-block';
        });
      });
      editBtn.addEventListener('click', () => {
        editQueueItem(item.id, queueState.queue, (id, prompt) => {
          queueState.editingId = id;
          const input = queryInput();
          const addButton = queryAddButton();
          if (input && addButton) {
            input.value = prompt;
            addButton.textContent = 'Save Changes';
            input.focus();
            input.selectionStart = input.selectionEnd = input.value.length;
          }
        });
      });
      deleteBtn.addEventListener('click', () => {
        deleteQueueItem(item.id, queueState.queue, renderGeminiQueue, saveGeminiQueue);
      });
      list.appendChild(li);
    });
    if (failedList && failedTitle) {
      while (failedList.firstChild) {
        failedList.removeChild(failedList.firstChild);
      }
      failedTitle.style.display = queueState.failedQueue.length > 0 ? 'block' : 'none';
      queueState.failedQueue.forEach((item) => {
        const li = document.createElement('li');
        li.style.marginBottom = '8px';
        li.style.color = '#ff9999';
        li.style.fontSize = '13px';
        const row = document.createElement('div');
        row.style.display = 'flex';
        row.style.gap = '6px';
        row.style.alignItems = 'flex-start';
        const text = document.createElement('div');
        text.textContent = item.prompt;
        text.style.flex = '1';
        text.style.wordBreak = 'break-word';
        const retryBtn = document.createElement('button');
        retryBtn.textContent = '\u{1F504}';
        retryBtn.title = 'Retry';
        retryBtn.style.cursor = 'pointer';
        retryBtn.style.color = '#7dd3fc';
        retryBtn.style.fontSize = '12px';
        retryBtn.addEventListener('click', () => {
          const index = queueState.failedQueue.findIndex((i) => i.id === item.id);
          if (index !== -1) {
            const [retryItem] = queueState.failedQueue.splice(index, 1);
            retryItem.attempts = 0;
            retryItem.status = 'queued';
            queueState.queue.push(retryItem);
            renderGeminiQueue();
            saveGeminiQueue();
          }
        });
        const deleteBtn = document.createElement('button');
        deleteBtn.textContent = '\u2715';
        deleteBtn.title = 'Delete';
        deleteBtn.style.cursor = 'pointer';
        deleteBtn.style.color = '#ff6b6b';
        deleteBtn.style.fontSize = '12px';
        deleteBtn.addEventListener('click', () => {
          deleteQueueItem(item.id, queueState.failedQueue, renderGeminiQueue, saveGeminiQueue);
        });
        row.appendChild(text);
        row.appendChild(retryBtn);
        row.appendChild(deleteBtn);
        li.appendChild(row);
        failedList.appendChild(li);
      });
    }
    updateToolbarButton(
      document.querySelector('#pq-toolbar-button'),
      queueState.queue,
      queueState.running
    );
    log('queue rendered safely');
  }
  function saveGeminiQueue() {
    saveQueue(queueState.queue, queueState.failedQueue, STORAGE_KEY, getCurrentGeminiChatCode());
    refreshChatManager(STORAGE_KEY);
  }
  function syncQueuedItemsToCurrentChatCode(chatCode) {
    if (!chatCode) return false;
    const updated = applyScopeToQueuedItems(queueState.queue, queueState.failedQueue, chatCode);
    if (!updated) return false;
    saveGeminiQueue();
    refreshChatManager(STORAGE_KEY);
    return true;
  }
  function loadGeminiQueue() {
    loadQueue(queueState.queue, queueState.failedQueue, STORAGE_KEY, getCurrentGeminiChatCode());
  }
  function openGeminiChatManager() {
    openChatManagerWindow(STORAGE_KEY, 'Gemini Chat Prompt Manager');
  }
  async function processGeminiQueue() {
    const panel = queryPanel();
    if (!panel) return;
    setStatus(panel, 'Running');
    while (queueState.queue.length > 0 && queueState.running) {
      await waitForIdle();
      const item = queueState.queue.shift();
      if (!item || typeof item.prompt !== 'string') {
        error('Skipping invalid queue item:', item);
        continue;
      }
      const prompt = item.prompt;
      const beforeChatCode = getCurrentGeminiChatCode();
      queueState.awaitingChatScopeSync = !beforeChatCode;
      updateToolbarButton(
        document.querySelector('#pq-toolbar-button'),
        queueState.queue,
        queueState.running
      );
      renderGeminiQueue();
      setStatus(panel, `Sending: ${prompt.slice(0, 40)}...`);
      try {
        await sendPrompt(prompt);
        const afterChatCode = getCurrentGeminiChatCode();
        if (!beforeChatCode && afterChatCode) {
          syncQueuedItemsToCurrentChatCode(afterChatCode);
        }
        if (afterChatCode) {
          queueState.awaitingChatScopeSync = false;
        }
        item.attempts = 0;
      } catch (err) {
        queueState.awaitingChatScopeSync = false;
        error('Failed to send prompt:', formatError(err));
        item.status = 'failed';
        item.attempts = (item.attempts || 0) + 1;
        if (item.attempts < 3) {
          item.status = 'queued';
          queueState.queue.push(item);
        } else {
          queueState.failedQueue.push(item);
        }
      }
      if (beforeChatCode) {
        queueState.awaitingChatScopeSync = false;
      }
      saveGeminiQueue();
    }
    setStatus(panel, queueState.running ? 'Finished' : 'Stopped');
    queueState.running = false;
    updateToolbarButton(
      document.querySelector('#pq-toolbar-button'),
      queueState.queue,
      queueState.running
    );
  }
  function ensureGeminiToolbarButton() {
    ensureToolbarStyles();
    installSelectionPromptMenu({
      createItem: geminiProvider.createItem,
      renderQueue: renderGeminiQueue,
      saveQueue: saveGeminiQueue,
      updateToolbarButton,
    });
    let button = document.querySelector('#pq-toolbar-button');
    if (!button) {
      button = document.createElement('button');
      button.id = 'pq-toolbar-button';
      button.classList.add('pq-toolbar');
      button.type = 'button';
      button.textContent = 'Queue';
      button.addEventListener('click', () => showPanel(() => createGeminiPanel()));
    }
    button.className = 'pq-toolbar';
    Object.assign(button.style, {
      position: 'fixed',
      bottom: '24px',
      right: '24px',
      padding: '10px 14px',
      borderRadius: '9999px',
      background: '#1f1f1f',
      color: '#fff',
      border: '1px solid #555',
      boxShadow: '0 10px 30px rgba(0,0,0,0.35)',
      zIndex: '2147483647',
      cursor: 'pointer',
    });
    if (button.parentElement !== document.body) {
      document.body.appendChild(button);
    }
  }
  var geminiProvider = {
    storageKey: STORAGE_KEY,
    includeFailedQueue: true,
    getCurrentScope: getCurrentGeminiChatCode,
    createItem(text) {
      const chatCode = getCurrentGeminiChatCode();
      return {
        id: crypto.randomUUID(),
        prompt: text,
        attempts: 0,
        status: 'queued',
        createdAt: Date.now(),
        ...(chatCode ? { chatCode } : {}),
      };
    },
    createPanel: createGeminiPanel,
    renderQueue: renderGeminiQueue,
    saveQueue: saveGeminiQueue,
    loadQueue: loadGeminiQueue,
    processQueue: processGeminiQueue,
    setupPanelControls,
    setupPanelDrag,
    ensureToolbarButton: ensureGeminiToolbarButton,
    openChatManager: openGeminiChatManager,
    isOwnMutation(target) {
      return !!target && (target.closest?.('#pq-panel') || target.closest?.('.pq-toolbar'));
    },
  };
  bootstrapQueueApp(geminiProvider);
})();
//# sourceMappingURL=gemini.user.js.map