🤖ChatGPT - Prompt提示选择器

一个帮助用户在ChatGPT原生网页快速选择 ChatGPT 提示"Prompt"的脚本。

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         🤖ChatGPT - Prompt提示选择器
// @namespace    https://greasyfork.org/
// @version      1.0.5
// @description  一个帮助用户在ChatGPT原生网页快速选择 ChatGPT 提示"Prompt"的脚本。
// @author       OpenAI - ChatGPT
// @require      https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.6.4.min.js
// @match        https://chat.openai.com/
// @match        https://chat.openai.com/c/*
// @match        https://chat.openai.com/?*
// @run-at       document-start
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @license      GNU GPLv3
// ==/UserScript==

(function () {
    "use strict";
    const $ = window.jQuery;
    const DARK_MODE_STYLE = `
    .dark-mode-compatible {
      background-color: white;
      color: black;
    }
    .dark .dark-mode-compatible {
      background-color: #343540;
      color: white;
    }

    select {
      font-size: 16px;
      border-radius: 4px;
    }

    select option {
      font-size: 16px;
      padding: 8px;
    }

  `;

    //可通过替换下面的Json链接来自定义提示库。
    const DATA_URL =
          "https://fs-im-kefu.7moor-fs1.com/29397395/4d2c3f00-7d4c-11e5-af15-41bf63ae4ea0/1681658246070/input.json";

    const CATEGORY_DROPDOWN_ID = "chatgpt-prompt-selector-category";
    const SUBCATEGORY_DROPDOWN_ID = "chatgpt-prompt-selector-subcategory";
    const CUSTOM_PROMPT_DROPDOWN_ID = "customPromptDropdown";
    const CATEGORY_DROPDOWN_PLACEHOLDER = "[类别]";
    const SUBCATEGORY_DROPDOWN_PLACEHOLDER = "[提示]";
    const CUSTOM_PROMPT_DROPDOWN_PLACEHOLDER = "[自定义提示]";

    const CATEGORY_DROPDOWN_WIDTH = 100;
    const SUBCATEGORY_DROPDOWN_WIDTH = 180;
    const CUSTOM_PROMPT_DROPDOWN_WIDTH = 180;
    const DEFAULT_ENTRY_HEIGHT = 20;

    const CHECK_MARKUP_INTERVAL = 500;
    const ENTRY_HEIGHT_RESET_DELAY = 500;

    const CONTAINER_SELECTOR = "form div.md\\:w-full.justify-center";
    const ENTRY_SELECTOR = "textarea";

    const SELECT_START_TAG_TEMPLATE =
          '<select id="{{id}}" class="dark-mode-compatible" style="width: {{width}}px">';
    const SELECT_END_TAG_TEMPLATE = "</select>";
    const OPTION_TAG_TEMPLATE =
          '<option value="{{value}}" title="{{titleMark}}">{{title}}</option>';
    const DATA_LOAD_ERROR_MESSAGE =
          "ChatGPT提示选择器错误:无法下载数据集请检查json链接是否正确。";

    const NEW_LINE = "\r\n";

    let promptItems = null;

    function placeholder(name) {
        return "{{" + name + "}}";
    }

    function createDropdown(parent, id, defaultOptionTitle, width, items) {
        let markup = SELECT_START_TAG_TEMPLATE.replace(
            placeholder("id"),
            id
        ).replace(placeholder("width"), width);

        markup += OPTION_TAG_TEMPLATE.replace(placeholder("value"), "")
            .replace(placeholder("title"), defaultOptionTitle)
            .replace(placeholder("titleMark"), "");

        for (const item of items) {
            const title = item.title;
            const mark = item.mark;
            markup += OPTION_TAG_TEMPLATE.replace(placeholder("value"), mark)
                .replace(placeholder("title"), title)
                .replace(placeholder("titleMark"), mark);
        }

        markup += SELECT_END_TAG_TEMPLATE;

        if (id === CATEGORY_DROPDOWN_ID) {
            parent.prepend(markup);
        } else {
            getCategoryDropdown().after(markup);
        }
    }

    function getCategoryDropdown() {
        return $("#" + CATEGORY_DROPDOWN_ID);
    }

    function getSubcategoryDropdown() {
        return $("#" + SUBCATEGORY_DROPDOWN_ID);
    }

    function getCustomPromptDropdown() {
        return $("#" + CUSTOM_PROMPT_DROPDOWN_ID);
    }

    function handleSubcategoryChange() {
        const categoryDropdown = getCategoryDropdown();
        const categoryMark = categoryDropdown.val();
        const categoryItem = promptItems.find((item) => item.mark === categoryMark);

        if (categoryItem === null) return;

        const subcategoryMark = $(this).val();
        const subcategoryItem = categoryItem.items.find(
            (item) => item.mark === subcategoryMark
        );

        if (subcategoryItem === null) return;

        const prompt = categoryItem.prompt.replace(
            placeholder("prompt"),
            subcategoryItem.prompt
        );
        const parts = prompt.split(NEW_LINE);
        const heightInPixels = DEFAULT_ENTRY_HEIGHT * parts.length + 1;

        const entry = $(ENTRY_SELECTOR);
        const button = entry.next();

        entry.height(heightInPixels);
        entry.val(prompt);

        button.prop("disabled", false);
        button.click(() => {
            setTimeout(() => {
                entry.height(DEFAULT_ENTRY_HEIGHT);
            }, ENTRY_HEIGHT_RESET_DELAY);
        });
        const defaultCategoryMark = categoryDropdown
        .find('option:contains("默认")')
        .val();
        categoryDropdown.val(defaultCategoryMark);
        categoryDropdown.trigger("change");
    }

    function handleCategoryChange() {
        const categoryMark = $(this).val();
        const categoryItem = promptItems.find((item) => item.mark === categoryMark);
        if (categoryItem === null) return;

        const dropdown = getSubcategoryDropdown();
        if (dropdown.length > 0) dropdown.remove();

        const container = getContainer();
        createDropdown(
            container,
            SUBCATEGORY_DROPDOWN_ID,
            SUBCATEGORY_DROPDOWN_PLACEHOLDER,
            SUBCATEGORY_DROPDOWN_WIDTH,
            categoryItem.items
        );

        const subcategoryDropdown = getSubcategoryDropdown();
        subcategoryDropdown.change(handleSubcategoryChange);
    }

    function handleCustomPromptChange() {
        const customPrompt = $(this).val();
        const entry = $(ENTRY_SELECTOR);
        const button = entry.next();

        entry.val(customPrompt);

        // 计算所选自定义提示的高度,并将其应用于输入框
        const parts = customPrompt.split(NEW_LINE);
        const heightInPixels = DEFAULT_ENTRY_HEIGHT * parts.length + 1;
        entry.height(heightInPixels);

        button.prop("disabled", false);
        button.click(() => {
            setTimeout(() => {
                entry.height(DEFAULT_ENTRY_HEIGHT);
            }, ENTRY_HEIGHT_RESET_DELAY);
        });
    }

    function activateCheckTimer() {
        setInterval(checkMarkup, CHECK_MARKUP_INTERVAL);
    }

    function getContainer() {
        return $(CONTAINER_SELECTOR);
    }

    function checkMarkup() {
        const container = getContainer();

        if (container.has("select").length > 0) return;
        if (window.innerWidth <= 480) return;

        createDropdown(
            container,
            CATEGORY_DROPDOWN_ID,
            CATEGORY_DROPDOWN_PLACEHOLDER,
            CATEGORY_DROPDOWN_WIDTH,
            promptItems
        );
        createDropdown(
            container,
            SUBCATEGORY_DROPDOWN_ID,
            SUBCATEGORY_DROPDOWN_PLACEHOLDER,
            SUBCATEGORY_DROPDOWN_WIDTH,
            []
        );
        const customPrompts = JSON.parse(GM_getValue("customPrompts", "[]"));
        createDropdown(
            container,
            CUSTOM_PROMPT_DROPDOWN_ID,
            CUSTOM_PROMPT_DROPDOWN_PLACEHOLDER,
            CUSTOM_PROMPT_DROPDOWN_WIDTH,
            customPrompts
        );
        const customPromptDropdown = getCustomPromptDropdown();
        customPromptDropdown.toggle(customPrompts.length > 0);
        customPromptDropdown.change(handleCustomPromptChange);

        const categoryDropdown = getCategoryDropdown();
        categoryDropdown.change(handleCategoryChange);
        const defaultCategoryMark = categoryDropdown
        .find('option:contains("默认")')
        .val();
        categoryDropdown.val(defaultCategoryMark);
        categoryDropdown.trigger("change");
        addCustomPromptButton();
    }

    function addCustomPrompt() {
        const title = prompt("请输入提示的标题:");
        if (!title) return;
        const customPrompts = JSON.parse(GM_getValue("customPrompts", "[]"));
        if (customPrompts.find((item) => item.title === title)) {
            alert("该标题已经存在,请输入一个新的标题。");
            return;
        }

        const content = prompt("请输入提示的内容:");
        if (!content) return;

        customPrompts.push({ title, mark: content });
        GM_setValue("customPrompts", JSON.stringify(customPrompts));

        const customPromptDropdown = getCustomPromptDropdown();
        customPromptDropdown.append(
            OPTION_TAG_TEMPLATE.replace(placeholder("value"), content)
            .replace(placeholder("title"), title)
            .replace(placeholder("titleMark"), content)
        );
        customPromptDropdown.val(content);
        customPromptDropdown.trigger("change");
        customPromptDropdown.show();
    }

    function removeCustomPrompt(mark) {
        let customPrompts = JSON.parse(GM_getValue("customPrompts", "[]"));
        customPrompts = customPrompts.filter((item) => item.mark !== mark);
        GM_setValue("customPrompts", JSON.stringify(customPrompts));
    }

    function addCustomPromptButton() {
        let addButton, removeButton;

        const container = getContainer();
        addButton = $(
            '<button class="dark-mode-compatible btn relative btn-neutral border-0 md:border" type="button">添加</button>'
        );
        addButton.click(addCustomPrompt);
        removeButton = $(
            '<button class="dark-mode-compatible btn relative btn-neutral border-0 md:border" type="button">删除</button>'
        );
        removeButton.click(() => {
            const customPromptDropdown = getCustomPromptDropdown();
            const mark = customPromptDropdown.val();
            if (!mark) return;
            customPromptDropdown.find(`option[value="${mark}"]`).remove();
            removeCustomPrompt(mark);
            if (customPromptDropdown.children().length <= 1) {
                customPromptDropdown.hide();
                removeButton.hide();
                addButton.show(); // 显示添加按钮
            }

            // 清空输入框
            const entry = $(ENTRY_SELECTOR);
            entry.val("");
        });
        removeButton.hide();
        getCustomPromptDropdown().after(removeButton);
        getCustomPromptDropdown().after(addButton);

        const customPromptDropdown = getCustomPromptDropdown();
        if (customPromptDropdown.children().length <= 1) {
            removeButton.hide();
            addButton.show(); // 显示添加按钮
        }

        customPromptDropdown.on("change", () => {
            if (customPromptDropdown.val()) {
                removeButton.show();
                addButton.hide(); // 隐藏添加按钮
            } else {
                removeButton.hide();
                addButton.show(); // 显示添加按钮
            }
        });
    }




    function createCustomPromptDropdown() {
        const container = getContainer();
        const customPrompts = JSON.parse(GM_getValue("customPrompts", "[]"));
        const customPromptDropdown = $(
            `<select id="${CUSTOM_PROMPT_DROPDOWN_ID}" class="${CATEGORY_DROPDOWN_ID}" style="width: ${CATEGORY_DROPDOWN_WIDTH}px" size="${customPrompts.length}"></select>`
      );
        container.append(customPromptDropdown);

        for (const customPrompt of customPrompts) {
            customPromptDropdown.append(
                `<option value="${customPrompt.mark}" title="${customPrompt.mark}">${customPrompt.title}</option>`
        );
        }

        if (customPrompts.length === 0) {
            customPromptDropdown.hide();
        } else {
            customPromptDropdown.show();
        }

        customPromptDropdown.change(() => {
            const selectedPrompt = customPromptDropdown.val();
            if (selectedPrompt) {
                const entry = $(ENTRY_SELECTOR);
                entry.val(selectedPrompt);
            }
        });

        return customPromptDropdown;
    }

    function setReceivedData(jsonText) {
        promptItems = JSON.parse(jsonText);
    }

    function loadData() {
        const getData = new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: DATA_URL,
                responseType: "text",
                overrideMimeType: "text/html; charset=UTF-8",
                onload: (response) => {
                    resolve(response.responseText);
                },
                onerror: () => {
                    reject(DATA_LOAD_ERROR_MESSAGE);
                },
            });

        });

        getData
            .then((data) => {
            setReceivedData(data);
            activateCheckTimer();
        })
            .catch((error) => {
            console.error(error);
        });
    }

    function addDarkModeStyles() {
        const styleTag = document.createElement("style");
        styleTag.innerHTML = DARK_MODE_STYLE;
        document.head.appendChild(styleTag);
    }

    createCustomPromptDropdown();
    activateCheckTimer();
    addDarkModeStyles();
    loadData();
    addCustomPromptButton();
})();