您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Bug Team —— 好用、爱用 ♥
当前为
// ==UserScript== // @name Switch Bug Team Model // @namespace http://tampermonkey.net/ // @version 1.0 // @description Bug Team —— 好用、爱用 ♥ // @author wandouyu // @match *://chatgpt.com/* // @match *://chat.openai.com/* // @match *://chat.voct.dev/* // @grant GM_addStyle // @license MIT // ==/UserScript== (function() { 'use strict'; const modelMap = { "o3 ": "o3", "o4-mini-high": "o4-mini-high", "o4-mini": "o4-mini", "gpt-4.5 (preview)": "gpt-4-5", "gpt-4o": "gpt-4o", "gpt-4o-mini": "gpt-4o-mini", "gpt-4o (tasks)": "gpt-4o-jawbone", "gpt-4": "gpt-4" }; const modelDisplayNames = Object.keys(modelMap); const modelIds = Object.values(modelMap); let dropdownElement = null; let isDropdownVisible = false; GM_addStyle(` .model-switcher-container { position: relative; display: inline-block; margin-left: 8px; } #model-switcher-button { display: inline-flex; align-items: center; justify-content: center; height: 36px; min-width: 36px; padding: 0 12px; border-radius: 9999px; border: 1px solid var(--token-border-light, #E5E5E5); font-size: 14px; font-weight: 500; color: var(--token-text-secondary, #666666); background-color: var(--token-main-surface-primary, #FFFFFF); cursor: pointer; white-space: nowrap; transition: background-color 0.2s ease; box-sizing: border-box; } #model-switcher-button:hover { background-color: var(--token-main-surface-secondary, #F7F7F8); } #model-switcher-dropdown { position: fixed; display: block; background-color: var(--token-main-surface-primary, white); border: 1px solid var(--token-border-medium, #E5E5E5); border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); z-index: 1050; min-width: 180px; max-height: 300px; overflow-y: auto; padding: 4px 0; } .model-switcher-item { display: block; padding: 8px 16px; color: var(--token-text-primary, #171717); text-decoration: none; white-space: nowrap; cursor: pointer; font-size: 14px; } .model-switcher-item:hover { background-color: var(--token-main-surface-secondary, #F7F7F8); } .model-switcher-item.active { font-weight: bold; } `); function getCurrentModelInfo() { const params = new URLSearchParams(window.location.search); const currentModelId = params.get('model'); let currentDisplayName = "Select Model"; let currentIndex = -1; if (currentModelId) { const index = modelIds.indexOf(currentModelId); if (index !== -1) { currentIndex = index; currentDisplayName = modelDisplayNames[index]; } else { currentDisplayName = `Model: ${currentModelId.substring(0, 10)}${currentModelId.length > 10 ? '...' : ''}`; currentIndex = -1; } } else { if (modelDisplayNames.length > 0) { currentDisplayName = modelDisplayNames[0]; currentIndex = 0; } } return { currentId: currentModelId, displayName: currentDisplayName, index: currentIndex }; } function createModelSwitcher() { if (modelDisplayNames.length === 0) { console.warn("Model Switcher: modelMap is empty. Cannot create switcher."); return null; } const container = document.createElement('div'); container.className = 'model-switcher-container'; container.id = 'model-switcher-container'; const button = document.createElement('button'); button.id = 'model-switcher-button'; button.type = 'button'; const dropdown = document.createElement('div'); dropdown.className = 'model-switcher-dropdown'; dropdown.id = 'model-switcher-dropdown'; const currentInfo = getCurrentModelInfo(); button.textContent = currentInfo.displayName; modelDisplayNames.forEach((name, index) => { const modelId = modelIds[index]; const item = document.createElement('a'); item.className = 'model-switcher-item'; item.textContent = name; item.dataset.modelId = modelId; item.href = '#'; if ((currentInfo.currentId && currentInfo.currentId === modelId) || (!currentInfo.currentId && index === 0)) { item.classList.add('active'); } item.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); const selectedModelId = e.target.dataset.modelId; if (selectedModelId) { const url = new URL(window.location.href); url.searchParams.set('model', selectedModelId); window.location.href = url.toString(); } hideDropdown(); }); dropdown.appendChild(item); }); button.addEventListener('click', (e) => { e.stopPropagation(); toggleDropdown(); }); container.appendChild(button); dropdownElement = dropdown; return container; } function showDropdown() { if (!dropdownElement || isDropdownVisible) return; const button = document.getElementById('model-switcher-button'); if (!button) return; const buttonRect = button.getBoundingClientRect(); const scrollX = window.scrollX || window.pageXOffset; const scrollY = window.scrollY || window.pageYOffset; document.body.appendChild(dropdownElement); isDropdownVisible = true; const dropdownHeight = dropdownElement.offsetHeight; const spaceAbove = buttonRect.top; const spaceBelow = window.innerHeight - buttonRect.bottom; const margin = 5; let top, left = buttonRect.left + scrollX; if (spaceAbove > dropdownHeight + margin || spaceAbove >= spaceBelow) { top = buttonRect.top + scrollY - dropdownHeight - margin; } else { top = buttonRect.bottom + scrollY + margin; } if (top < scrollY + margin) top = scrollY + margin; if (left < scrollX + margin) left = scrollX + margin; const dropdownWidth = dropdownElement.offsetWidth; if (left + dropdownWidth > window.innerWidth + scrollX - margin) { left = window.innerWidth + scrollX - dropdownWidth - margin; } dropdownElement.style.top = `${top}px`; dropdownElement.style.left = `${left}px`; document.addEventListener('click', handleClickOutside, true); window.addEventListener('resize', hideDropdown); window.addEventListener('scroll', hideDropdown, true); } function hideDropdown() { if (!dropdownElement || !isDropdownVisible) return; if (dropdownElement.parentNode === document.body) { document.body.removeChild(dropdownElement); } isDropdownVisible = false; document.removeEventListener('click', handleClickOutside, true); window.removeEventListener('resize', hideDropdown); window.removeEventListener('scroll', hideDropdown, true); } function toggleDropdown() { if (isDropdownVisible) { hideDropdown(); } else { showDropdown(); } } function handleClickOutside(event) { const button = document.getElementById('model-switcher-button'); if (dropdownElement && dropdownElement.parentNode === document.body && button && !button.contains(event.target) && !dropdownElement.contains(event.target)) { hideDropdown(); } } function findCommentNode(parentElement, commentText) { const iterator = document.createNodeIterator(parentElement, NodeFilter.SHOW_COMMENT); let currentNode; while (currentNode = iterator.nextNode()) { if (currentNode.nodeValue.trim() === commentText) { return currentNode; } } return null; } function insertSwitcherButton() { const existingContainer = document.getElementById('model-switcher-container'); if (existingContainer) { const button = document.getElementById('model-switcher-button'); const currentInfo = getCurrentModelInfo(); if(button && button.textContent !== currentInfo.displayName) { button.textContent = currentInfo.displayName; if (dropdownElement) { const items = dropdownElement.querySelectorAll('.model-switcher-item'); items.forEach((item, index) => { item.classList.remove('active'); const modelId = item.dataset.modelId; if ((currentInfo.currentId && currentInfo.currentId === modelId) || (!currentInfo.currentId && index === 0)) { item.classList.add('active'); } }); } } return true; } const switcherContainer = createModelSwitcher(); if (!switcherContainer) return false; const toolbar = document.querySelector('.max-xs\\:gap-1.flex.items-center.gap-2.overflow-x-auto'); if (toolbar) { const commentNode = findCommentNode(toolbar, 'Insert code here'); if (commentNode && commentNode.parentNode) { commentNode.parentNode.insertBefore(switcherContainer, commentNode); console.log('Model Switcher: Button inserted before comment.'); return true; } } const toolsButton = document.querySelector('button[aria-label="Use a tool"]'); const toolsButtonWrapper = toolsButton?.closest('div[class*="relative"]'); if (toolsButtonWrapper && toolsButtonWrapper.parentNode && toolbar && toolbar.contains(toolsButtonWrapper)) { toolsButtonWrapper.parentNode.insertBefore(switcherContainer, toolsButtonWrapper); console.warn('Model Switcher: Comment not found. Inserted button before potential Tools button container.'); return true; } if (toolbar) { toolbar.appendChild(switcherContainer); console.warn('Model Switcher: Comment and specific Tools container not found. Appended button to toolbar.'); return true; } const composerArea = document.querySelector('textarea[tabindex="0"]')?.parentNode?.parentNode; if (composerArea) { console.warn('Model Switcher: Toolbar not found. Attempting insertion near composer (may fail).'); } console.error('Model Switcher: Could not find a suitable insertion point for the button.'); return false; } let insertionAttempted = false; const observer = new MutationObserver((mutationsList, obs) => { const targetParentExists = document.querySelector('.max-xs\\:gap-1.flex.items-center.gap-2.overflow-x-auto') || document.querySelector('button[aria-label="Use a tool"]')?.closest('div'); if (targetParentExists) { if (!document.getElementById('model-switcher-container')) { if (insertSwitcherButton()) { insertionAttempted = true; console.log("Model Switcher: Button check/insertion successful."); } else if (!insertionAttempted) { console.error('Model Switcher: Found toolbar area, but failed to insert button container.'); insertionAttempted = true; } } else { insertSwitcherButton(); insertionAttempted = true; } } if (insertionAttempted && !document.getElementById('model-switcher-container')) { console.log("Model Switcher: Button container removed by UI update, attempting re-insertion..."); insertionAttempted = false; hideDropdown(); setTimeout(insertSwitcherButton, 200); } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(insertSwitcherButton, 1500); })();