// ==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();
})();