油猴脚本:便于Google AI Studio移动端用户的快捷操作栏

4月20日开始Google one(Gemini Pro套餐)在Google AI Studio有更多额度了,在此分享一个本人自制移动端用户专用的油猴脚本。 经常在移动端用Google AI Studio的佬友应该都懂,填充个提示词需要点好几个地方,开启新对话按钮太靠上点击费力,还有不打开侧边栏看不...
油猴脚本:便于Google AI Studio移动端用户的快捷操作栏
油猴脚本:便于Google AI Studio移动端用户的快捷操作栏

4月20日开始Google one(Gemini Pro套餐)在Google AI Studio有更多额度了,在此分享一个本人自制移动端用户专用的油猴脚本
经常在移动端用Google AI Studio的佬友应该都懂,填充个提示词需要点好几个地方,开启新对话按钮太靠上点击费力,还有不打开侧边栏看不见当前所选的模型,这个脚本就是解决这个问题的,我在输入框上方集成了这些功能的按钮,具体效果如图所示。

Screenshot2026-04-21-20-03-55-472com.xbrowser.play

// ==UserScript==
// @name         Google AI Studio 移动端快捷操作栏
// @namespace    https://tampermonkey.net/
// @version      5.6
// @description  “NEW”按钮用于开启新对话窗口,“ON”按钮用于打开填充System instructions面板,“OFF”按钮用于关闭System instructions面板;按钮上方横列显示当前模型名称。
// @match        https://aistudio.google.com/*
// @run-at       document-idle
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  const SCAN_INTERVAL = 500;
  const GAP_TO_INPUT = 20;

  const wrap = document.createElement('div');
  wrap.id = 'aiStudioCustomBtnGroup';

  const css = document.createElement('style');
  css.textContent = `
    #aiStudioCustomBtnGroup {
      position: fixed;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 5px;
      z-index: 2147483647;
      pointer-events: none;
      transform: translate(-50%, -100%);
      transition: opacity 0.2s ease;
      opacity: 0;
      padding-bottom: 2px;
    }

    #custom-model-name {
      font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', sans-serif;
      font-size: 10.5px;
      font-weight: 600;
      letter-spacing: 0.04em;
      color: rgba(50, 42, 36, 0.88);
      white-space: nowrap;
      pointer-events: none;
      user-select: none;
      padding: 3px 10px;
      background: rgba(255, 248, 242, 0.82);
      border-radius: 20px;
      border: 0.5px solid rgba(180, 140, 110, 0.25);
      margin-bottom: 1px;
    }

    .custom-btn-row {
      display: flex;
      gap: 6px;
      pointer-events: none;
      align-items: center;
      justify-content: center;
    }

    .custom-circle-btn {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 34px;
      height: 34px;
      border-radius: 50%;
      color: #fff;
      font-weight: 700;
      font-size: 11px;
      letter-spacing: 0.04em;
      font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', sans-serif;
      border: none;
      pointer-events: auto;
      cursor: pointer;
      user-select: none;
      transition: transform 0.12s ease, filter 0.12s ease;
      padding: 0;
      margin: 0;
      -webkit-font-smoothing: antialiased;
      background: #CC785C;
      box-shadow: 0 1px 6px rgba(180, 90, 60, 0.4);
    }

    .custom-circle-btn:hover {
      transform: scale(1.07);
      filter: brightness(1.1);
    }

    .custom-circle-btn:active {
      transform: scale(0.92);
      filter: brightness(0.88);
    }
  `;
  document.head.appendChild(css);

  const modelLabel = document.createElement('div');
  modelLabel.id = 'custom-model-name';
  modelLabel.textContent = '—';

  const makeBtn = (id, txt) => {
    const el = document.createElement('button');
    el.id = id;
    el.className = 'custom-circle-btn';
    el.textContent = txt;
    return el;
  };

  const btnNew = makeBtn('custom-btn-new', 'NEW');
  const btnOn  = makeBtn('custom-btn-on',  'ON');
  const btnOff = makeBtn('custom-btn-off', 'OFF');

  const rowNew = document.createElement('div');
  rowNew.className = 'custom-btn-row';
  rowNew.appendChild(btnNew);

  const rowOnOff = document.createElement('div');
  rowOnOff.className = 'custom-btn-row';
  rowOnOff.append(btnOn, btnOff);

  wrap.append(modelLabel, rowNew, rowOnOff);
  document.body.appendChild(wrap);

  const updatePosition = () => {
    let targetInput = null;
    const selectors = ['[placeholder*="Start typing a prompt"]', '[placeholder*="Optional tone"]'];

    for (const sel of selectors) {
      const el = document.querySelector(sel);
      if (el && el.getBoundingClientRect().width > 0) {
        targetInput = el;
        break;
      }
    }

    if (targetInput) {
      const rect = targetInput.getBoundingClientRect();
      if (rect.top > 0) {
        wrap.style.opacity = '1';
        wrap.style.left = `${window.innerWidth / 2}px`;
        wrap.style.top = `${rect.top - GAP_TO_INPUT}px`;
      } else {
        wrap.style.opacity = '0';
      }
    } else {
      wrap.style.opacity = '0';
    }
    requestAnimationFrame(updatePosition);
  };

  const attachToTopLayer = () => {
    try {
      const vw = window.innerWidth;
      const vh = window.innerHeight;
      let bestEl = document.body;
      let bestZ = -Infinity;

      const all = document.body.querySelectorAll('*');
      for (let i = 0; i < all.length; i++) {
        const el = all[i];
        if (el === wrap) continue;
        const rect = el.getBoundingClientRect();
        if (rect.width >= vw * 0.9 && rect.height >= vh * 0.9 && rect.left <= vw * 0.1 && rect.top <= vh * 0.1) {
          const style = getComputedStyle(el);
          if ((style.position === 'fixed' || style.position === 'absolute') && style.visibility !== 'hidden' && style.display !== 'none') {
            let zi = parseInt(style.zIndex, 10);
            if (Number.isNaN(zi)) zi = 0;
            if (zi >= bestZ) { bestZ = zi; bestEl = el; }
          }
        }
      }
      if (!bestEl.contains(wrap) || bestEl.lastElementChild !== wrap) {
        bestEl.appendChild(wrap);
      }
    } catch (e) {}
  };

  const readModel = () => {
    try {
      const raw = localStorage.getItem('aiStudioUserPreference');
      if (!raw) return '—';
      const pm = JSON.parse(raw)?.promptModel;
      return pm ? pm.replace('models/', '') : '—';
    } catch (e) {
      return '—';
    }
  };

  let lastModel = '';
  const tickModel = () => {
    const now = readModel();
    if (now !== lastModel) { modelLabel.textContent = now; lastModel = now; }
  };

  requestAnimationFrame(updatePosition);
  setInterval(attachToTopLayer, SCAN_INTERVAL);
  tickModel();
  setInterval(tickModel, 1000);

  btnNew.addEventListener('click', () => {
    document.querySelector('a[href*="prompts/new_chat"]')?.click();
    setTimeout(() => {
      document.querySelector("button[data-test-category-id='17']")?.click();
    }, 50);
  });

  btnOn.addEventListener('click', () => {
    const sysBtn = document.querySelector("button[aria-label='System instructions']");
    if (sysBtn) {
      sysBtn.click();
    } else {
      document.querySelector("button[aria-label='Toggle run settings panel']")?.click();
      setTimeout(() => {
        document.querySelector("button[aria-label='System instructions']")?.click();
      }, 300);
    }
  });

  btnOff.addEventListener('click', () => {
    document.querySelector("button[aria-label='关闭面板'], button[aria-label='Close panel'], button[aria-label='Close']")?.click();
  });
})();

1 个帖子 - 1 位参与者

阅读完整话题

来源: linux.do查看原文