您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
OpenAI playground accessibility enhancements with keyboard navigation. Quickly edit/delete/expand messages.
当前为
- // ==UserScript==
- // @name OpenAI Playground Hotkeys and Accessibility Enhancements
- // @namespace http://tampermonkey.net/
- // @license MIT
- // @author Rekt
- // @version 1.1.0
- // @description OpenAI playground accessibility enhancements with keyboard navigation. Quickly edit/delete/expand messages.
- // @match https://platform.openai.com/playground/prompts*
- // @grant none
- // ==/UserScript==
- (function() {
- 'use strict';
- // Tracks if user is editing a message
- let isEditing = false;
- // --- Message Box and Action Selectors ---
- function getMessageBoxes() {
- return Array.from(document.querySelectorAll("div.LT9iz > div > div > div:nth-child(1) > div"));
- }
- function getButtonsBarInMessageBox(msgBox) {
- return msgBox.querySelector(".rqgky > ._5qprQ");
- }
- function getButtonsInMessageBox(msgBox) {
- let btnBar = getButtonsBarInMessageBox(msgBox);
- if (!btnBar) return [];
- return Array.from(btnBar.querySelectorAll("button")).filter(btn => btn.offsetParent !== null);
- }
- // --- Message Type Detection ---
- function isAssistantMessage(msgBox) {
- let roleElement = msgBox.querySelector("div");
- if (roleElement && roleElement.innerText) {
- return roleElement.innerText.toLowerCase().includes("assistant");
- }
- let boxes = getMessageBoxes();
- let idx = boxes.indexOf(msgBox);
- return idx !== -1 && idx % 2 === 1;
- }
- // --- Robust Edit Button Selector ---
- function getEditButton(msgBox) {
- let btnBar = getButtonsBarInMessageBox(msgBox);
- if (!btnBar) return null;
- let isAssistant = isAssistantMessage(msgBox);
- let btns = getButtonsInMessageBox(msgBox);
- if (isAssistant) {
- let btn = btnBar.querySelector("button:nth-child(1)");
- if (btn && btn.offsetParent !== null) return btn;
- } else {
- let btn = btnBar.querySelector("button:nth-child(4)");
- if (btn && btn.offsetParent !== null) return btn;
- }
- for (let btn of btns) {
- let svg = btn.querySelector("svg");
- let ariaLabel = svg ? svg.getAttribute("aria-label") : "";
- if (
- (btn.innerText && btn.innerText.toLowerCase().includes('edit')) ||
- (btn.title && btn.title.toLowerCase().includes('edit')) ||
- (ariaLabel && ariaLabel.toLowerCase().includes('edit'))
- ) {
- return btn;
- }
- }
- return null;
- }
- // --- Delete Button Selector ---
- function getDeleteButton(msgBox) {
- let btnBar = getButtonsBarInMessageBox(msgBox);
- if (!btnBar) return null;
- let btns = getButtonsInMessageBox(msgBox);
- for (let btn of btns) {
- let svg = btn.querySelector("svg");
- let ariaLabel = svg ? svg.getAttribute("aria-label") : "";
- if (
- (btn.innerText && btn.innerText.toLowerCase().includes('delete')) ||
- (btn.title && btn.title.toLowerCase().includes('delete')) ||
- (ariaLabel && ariaLabel.toLowerCase().includes('delete')) ||
- (ariaLabel && ariaLabel.toLowerCase().includes('trash'))
- ) {
- return btn;
- }
- }
- if (btns.length) return btns[btns.length - 1];
- return null;
- }
- // --- Expand Button Selector ---
- function getExpandButton(msgBox) {
- let btn = msgBox.querySelector(".G3JSV .BODGE > button");
- return btn && btn.offsetParent !== null ? btn : null;
- }
- // --- Keyboard + Click State ---
- let selectedIdx = 0;
- // --- Highlight/Select Logic ---
- function highlightSelected(noScroll) {
- const boxes = getMessageBoxes();
- boxes.forEach((box, idx) => {
- box.style.outline = "";
- box.style.zIndex = "";
- box.dataset.vimnavSelected = "";
- });
- if (boxes[selectedIdx]) {
- if (!noScroll) boxes[selectedIdx].scrollIntoView({block: "center", behavior: "smooth"});
- boxes[selectedIdx].style.outline = "2px solid #00ffd0";
- boxes[selectedIdx].style.zIndex = "10";
- boxes[selectedIdx].dataset.vimnavSelected = "1";
- }
- }
- // --- Input Blocking ---
- function notInInput() {
- const ae = document.activeElement;
- if (!ae) return true;
- if (ae.matches("input, textarea, select")) return false;
- if (ae.closest("[contenteditable='true']")) return false;
- return true;
- }
- function inContentEditable() {
- const ae = document.activeElement;
- if (!ae) return false;
- if (ae.closest("[contenteditable='true']")) return true;
- return false;
- }
- // --- Move Selection ---
- function moveSelection(delta) {
- const boxes = getMessageBoxes();
- if (!boxes.length) return;
- selectedIdx = Math.max(0, Math.min(selectedIdx + delta, boxes.length - 1));
- highlightSelected();
- }
- // --- Toggle Edit for Selected Message ---
- function toggleEditOfSelectedMessage() {
- const boxes = getMessageBoxes();
- if (!boxes.length || selectedIdx >= boxes.length) return;
- const box = boxes[selectedIdx];
- const btn = getEditButton(box);
- if (btn) {
- btn.click();
- }
- }
- // --- Toggle Expand for Selected Message ---
- function expandSelectedMessage() {
- const boxes = getMessageBoxes();
- if (!boxes.length || selectedIdx >= boxes.length) return;
- const box = boxes[selectedIdx];
- const btn = getExpandButton(box);
- if (btn) btn.click();
- }
- // --- Keyboard Navigation, Expand/Contract, Toggle Edit, Delete ---
- document.addEventListener("keydown", function(e) {
- if (e.key === 'Escape' && inContentEditable()) {
- toggleEditOfSelectedMessage();
- e.preventDefault();
- return;
- }
- if (isEditing) return; // Prevent navigation when editing
- if (!notInInput()) return;
- const boxes = getMessageBoxes();
- if (!boxes.length) return;
- if (e.key === 'j' || e.key === 'ArrowDown') {
- moveSelection(+1);
- e.preventDefault();
- } else if (e.key === 'k' || e.key === 'ArrowUp') {
- moveSelection(-1);
- e.preventDefault();
- } else if (e.key === 'e') {
- toggleEditOfSelectedMessage();
- e.preventDefault();
- } else if (e.key === 'x' || e.key === 'Delete') {
- let box = boxes[selectedIdx];
- let btn = getDeleteButton(box);
- if (btn) {
- btn.click();
- e.preventDefault();
- }
- } else if (e.key === ' ' || e.key === 'Spacebar') {
- if (!inContentEditable()) {
- expandSelectedMessage();
- e.preventDefault();
- }
- }
- });
- // --- Click/DoubleClick for Selection & Expanding ---
- function handleMsgBoxClick(e, idx) {
- selectedIdx = idx;
- highlightSelected(true);
- }
- function handleMsgBoxDoubleClick(e, idx) {
- selectedIdx = idx;
- highlightSelected(true);
- expandSelectedMessage();
- }
- // --- On New Messages/UI Mutation: Maintain Highlight, Re-install Click Listeners ---
- function ensureHighlightAndListeners() {
- const boxes = getMessageBoxes();
- boxes.forEach((box, idx) => {
- if (!box.dataset.vimnavClickSet) {
- box.addEventListener('click', e => handleMsgBoxClick(e, idx));
- box.addEventListener('dblclick', e => handleMsgBoxDoubleClick(e, idx));
- box.dataset.vimnavClickSet = '1';
- }
- // Add focus/blur listeners to editable elements within message boxes
- const editableElements = box.querySelectorAll("[contenteditable='true'], input, textarea");
- editableElements.forEach(el => {
- el.addEventListener('focus', () => {
- isEditing = true;
- });
- el.addEventListener('blur', () => {
- isEditing = false;
- });
- });
- });
- if (selectedIdx >= boxes.length) selectedIdx = boxes.length - 1;
- if (selectedIdx < 0) selectedIdx = 0;
- highlightSelected(true);
- }
- // Initial Setup
- setTimeout(ensureHighlightAndListeners, 1500);
- // Observe DOM Changes
- const observer = new MutationObserver(ensureHighlightAndListeners);
- observer.observe(document.body, {childList: true, subtree: true});
- // Expose for Debugging
- window.OpenAIPlaygroundMsgNav = {
- getMessageBoxes,
- highlightSelected,
- moveSelection,
- getEditButton,
- getDeleteButton,
- expandSelectedMessage,
- toggleEditOfSelectedMessage
- };
- })();