您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a button to quickly copy question prompts from Google Forms, differentiating between question types.
// ==UserScript== // @name Google Forms - Prompt Copier // @namespace http://tampermonkey.net/ // @version 1.3 // @description Adds a button to quickly copy question prompts from Google Forms, differentiating between question types. // @author Nyxiereal // @match https://docs.google.com/forms/d/e/*/viewform* // @grant GM_setClipboard // @grant GM_addStyle // @license GPLv3 // ==/UserScript== (function() { 'use strict'; // Add styles for our custom button GM_addStyle(` .gform-copy-button { margin-left: 12px; padding: 2px 8px; font-size: 12px; font-weight: bold; color: #fff; background-color: #1a73e8; /* Google's primary button blue */ border: none; border-radius: 4px; cursor: pointer; vertical-align: middle; /* Align with the question text */ transition: background-color 0.2s; } .gform-copy-button:hover { background-color: #287ae6; } .gform-copy-button:disabled { background-color: #8ab4f8; /* Lighter blue for disabled state */ cursor: default; } `); /** * Handles the click event on a "Copy Prompt" button. * It determines the question type, formats the text, and copies it to the clipboard. * @param {MouseEvent} event - The click event. */ function handleCopyClick(event) { event.preventDefault(); const button = event.target; // Find the root container for the question this button belongs to. // 'div[jsmodel]' is a stable identifier for a question block. const questionBlock = button.closest('div[jsmodel]'); if (!questionBlock) return; // Find the question title element const questionTitleEl = questionBlock.querySelector('div[role="heading"] > span:first-child'); const questionTitle = questionTitleEl ? questionTitleEl.textContent.trim() : 'Question not found'; let promptText = ''; let questionType = 'Unknown Type'; let options = []; // 1. Check for Radio Buttons (Single Choice) const radioOptions = questionBlock.querySelectorAll('div[role="radio"]'); if (radioOptions.length > 0) { questionType = 'Single Choice'; radioOptions.forEach(radio => { // 'aria-label' is a reliable, accessibility-focused attribute for the option text. const label = radio.getAttribute('aria-label'); if (label) options.push(`- ${label}`); }); } // 2. If not radio, check for Checkboxes (Multi Choice) else { const checkOptions = questionBlock.querySelectorAll('div[role="checkbox"]'); if (checkOptions.length > 0) { questionType = 'Multi Choice (Select all that apply)'; checkOptions.forEach(check => { // 'aria-label' or 'data-answer-value' can hold the text. const label = check.getAttribute('aria-label') || check.getAttribute('data-answer-value'); if (label) options.push(`- ${label}`); }); } // 3. If not radio or checkbox, check for a text input area (Free Text) else { // Short answers use <input type="text">, long answers use <textarea>. // Both have a role of 'textbox'. const textInput = questionBlock.querySelector('div[role="textbox"], textarea, input[type="text"]'); if (textInput) { questionType = 'Free Text'; // No options for free text questions. } } } // Build the final string to be copied promptText = `[${questionType}]\nQuestion: ${questionTitle}`; if (options.length > 0) { promptText += '\n\nOptions:\n' + options.join('\n'); } // ADDED: The instruction text to be appended to the prompt. const instructionText = "Reply with the correct answer and a quick reasoning why it is the correct answer. Don't over-complicate stuff."; // MODIFIED: Append the new instruction text to the promptText string. promptText += `\n\n${instructionText}`; // Copy the generated prompt to the clipboard GM_setClipboard(promptText); // Provide visual feedback to the user const originalText = button.textContent; button.textContent = 'Copied!'; button.disabled = true; setTimeout(() => { button.textContent = originalText; button.disabled = false; }, 1500); } /** * Scans the document for question blocks and adds a "Copy Prompt" button if one doesn't already exist. * This function is designed to be run multiple times safely. */ function addCopyButtons() { // A 'div[jsmodel]' that is a direct child of a 'div[role="listitem"]' is a very reliable // selector for a question block. const questionBlocks = document.querySelectorAll('div[role="listitem"] > div[jsmodel]'); questionBlocks.forEach(block => { // Use a data attribute to mark blocks we've already processed to avoid adding duplicate buttons. if (block.dataset.copyButtonAdded) return; block.dataset.copyButtonAdded = 'true'; // Find the heading area where the question title lives. const headingContainer = block.querySelector('div[role="heading"]'); if (!headingContainer) return; // Not a standard question block (e.g., a description block) const button = document.createElement('button'); button.textContent = 'Copy'; button.className = 'gform-copy-button'; button.onclick = handleCopyClick; // Add the button right next to the question title. headingContainer.appendChild(button); }); } // --- Main Execution --- // Google Forms can load content dynamically, especially in multi-page forms. // A MutationObserver is the most reliable way to detect when new questions are added to the DOM. const observer = new MutationObserver((mutations) => { // We don't need to inspect the mutations, just re-run our function // to find any new question blocks that need a button. addCopyButtons(); }); // Start observing the entire document body for changes to the element tree. observer.observe(document.body, { childList: true, subtree: true }); // Run it once on initial load as well. addCopyButtons(); })();