您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Help organize commonly used spells quickly
当前为
// ==UserScript== // @name ChatGPT-input-helper // @name:zh-TW ChatGPT-input-helper 快速輸入常用咒文 // @namespace https://github.com/we684123/ChatGPT-input-helper // @version 0.0.5 // @author we684123 // @description Help organize commonly used spells quickly // @description:zh-TW 幫助快速組織常用咒文 // @license MIT // @icon https://chat.openai.com/favicon.ico // @match https://chat.openai.com/chat // @match https://chat.openai.com/chat/* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @run-at document-end // ==/UserScript== (function (factory) { typeof define === 'function' && define.amd ? define(factory) : factory(); })((function () { 'use strict'; const sentinel = (() => { const isArray = Array.isArray; let selectorToAnimationMap = {}; let animationCallbacks = {}; let styleEl; let styleSheet; let cssRules; return { // `on` 方法用於添加 CSS 選擇器的監聽器。 // cssSelectors: 一個字符串或字符串數組,包含要監聽的 CSS 選擇器。 // callback: 用於處理觸發的事件的回調函數。 on: function (cssSelectors, callback) { // 如果沒有提供回調函數,則直接返回。 if (!callback) return; // 如果 `styleEl` 未定義,創建一個新的 `style` 標籤並將其添加到文檔的 `head` 中。 // 還會為 `animationstart` 事件添加事件監聽器。 if (!styleEl) { const doc = document; const head = doc.head; doc.addEventListener("animationstart", function (ev) { const callbacks = animationCallbacks[ev.animationName]; if (!callbacks) return; ev.stopImmediatePropagation(); for (const cb of callbacks) { cb(ev.target); } }, true); styleEl = doc.createElement("style"); // head.insertBefore(styleEl, head.firstChild); // 這個是原版的,改用下面的 head.append(styleEl); // 感謝 chatgpt-exporter 搞好久 (┬┬﹏┬┬) styleSheet = styleEl.sheet; cssRules = styleSheet.cssRules; } // 根據提供的選擇器創建一個新的動畫。 const selectors = isArray(cssSelectors) ? cssSelectors : [cssSelectors]; selectors.forEach((selector) => { // 獲取或創建動畫 ID。 let animIds = selectorToAnimationMap[selector]; if (!animIds) { const isCustomName = selector[0] == "!"; const animId = isCustomName ? selector.slice(1) : "sentinel-" + Math.random().toString(16).slice(2); // 創建新的 keyframes 規則。 const keyframeRule = cssRules[styleSheet.insertRule("@keyframes " + animId + "{from{transform:none;}to{transform:none;}}", cssRules.length)]; keyframeRule._id = selector; // 如果選擇器不是自定義名稱,則為其創建對應的CSS 規則。 if (!isCustomName) { const selectorRule = cssRules[styleSheet.insertRule(selector + "{animation-duration:0.0001s;animation-name:" + animId + ";}", cssRules.length)]; selectorRule._id = selector; } animIds = [animId]; selectorToAnimationMap[selector] = animIds; } // 遍歷動畫 ID,將回調函數添加到動畫回調列表中。 animIds.forEach((animId) => { animationCallbacks[animId] = animationCallbacks[animId] || []; animationCallbacks[animId].push(callback); }); }); }, // `off` 方法用於移除 CSS 選擇器的監聽器。 // cssSelectors: 一個字符串或字符串數組,包含要停止監聽的 CSS 選擇器。 // callback: 可選的回調函數。如果提供,則僅移除與之匹配的監聽器。 off: function (cssSelectors, callback) { // 將提供的選擇器轉換為數組形式。 const selectors = isArray(cssSelectors) ? cssSelectors : [cssSelectors]; // 遍歷選擇器,移除對應的監聽器。 selectors.forEach((selector) => { const animIds = selectorToAnimationMap[selector]; if (!animIds) return; animIds.forEach((animId) => { const callbacks = animationCallbacks[animId]; if (!callbacks) return; // 如果提供了回調函數,則僅移除與之匹配的監聽器。 if (callback) { const index = callbacks.indexOf(callback); if (index !== -1) { callbacks.splice(index, 1); } } else { delete animationCallbacks[animId]; } // 如果該選擇器沒有任何回調函數,則從選擇器映射和 CSS 規則中移除它。 if (callbacks.length === 0) { delete selectorToAnimationMap[selector]; const rulesToDelete = []; for (let i = 0, len = cssRules.length; i < len; i++) { const rule = cssRules[i]; if (rule._id === selector) { rulesToDelete.push(rule); } } rulesToDelete.forEach((rule) => { const index = Array.prototype.indexOf.call(cssRules, rule); if (index !== -1) { styleSheet.deleteRule(index); } }); } }); }); } }; })(); function onloadSafe(fn) { if (document.readyState === "complete") { fn(); } else { window.addEventListener("load", fn); } } function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z$1 = ".buttonStyles-module_container__l-r9Y{align-items:center;border:1px solid #fff;border-radius:5px;box-sizing:border-box;display:flex;justify-content:center;position:relative;width:100%}.buttonStyles-module_mainButton__b08pW{border:1px solid #fff;border-radius:5px;margin:0 auto;padding:8px 12px;width:85%}.buttonStyles-module_mainButton__b08pW,.buttonStyles-module_settingButton__-opQi{background-color:#202123;box-sizing:border-box;color:#fff;cursor:pointer;font-size:14px}.buttonStyles-module_settingButton__-opQi{border:none;border-radius:5px;padding:8px 14px;width:15%}.buttonStyles-module_menu__aeYDY{background-color:#202123;border:1px solid #fff;border-radius:15px;display:none;left:100px;position:absolute;width:100%;z-index:1}.buttonStyles-module_menuButton__eg9D8{background-color:#202123;border:1px solid #fff;border-radius:5px;color:#fff;cursor:pointer;display:block;font-size:14px;height:100%;padding:8px 12px;width:100%}"; var styles = {"container":"buttonStyles-module_container__l-r9Y","mainButton":"buttonStyles-module_mainButton__b08pW","settingButton":"buttonStyles-module_settingButton__-opQi","menu":"buttonStyles-module_menu__aeYDY","menuButton":"buttonStyles-module_menuButton__eg9D8"}; styleInject(css_248z$1); // library.ts const config = { name: "aims-helper", init_customize: [ { name: '繁體中文初始化', position: 'start', autoEnter: true, content: [ `以下問答請使用繁體中文,並使用台灣用語。\n`, ].join("") }, { name: '請繼續', position: 'start', autoEnter: true, content: [ `請繼續`, ].join("") }, { name: '請從""繼續', position: 'start', autoEnter: false, content: [ `請從""繼續`, ].join("") } ], // ↓ 左邊選單的定位(上層) NAV_MENU: 'nav > div.overflow-y-auto', // ↓ 輸入框的定位 TEXT_INPUTBOX_POSITION: 'textarea.m-0', // ↓ 送出按鈕的定位 SUBMIT_BUTTON_POSITION: 'button.absolute', // ↓ 選單按鈕 MAIN_BUTTON_CLASS: 'main_button', // ↓ 控制按鈕 SETTING_BUTTON_CLASS: 'setting_button', // ↓ 選單 MENU_CLASS: 'main_menu', // ↓ 按鈕文字 HELPER_MENU_TEXT: 'input helper', // ↓ 按鈕用容器 CONTAINER_CLASS: 'helper_textcontainer', // ↓ 模擬輸入於輸入框的事件 INPUT_EVENT: new Event('input', { bubbles: true }), }; // 將自定義內容插入到輸入框中 const insertCustomize = (customize, name) => { const textInputbox = document.querySelector(config.TEXT_INPUTBOX_POSITION); const submitButton = document.querySelector(config.SUBMIT_BUTTON_POSITION); const item = customize.find((i) => i.name === name); if (item) { if (item.position === 'start') { textInputbox.value = item.content + textInputbox.value; } else { textInputbox.value += item.content; } textInputbox.dispatchEvent(config.INPUT_EVENT); textInputbox.focus(); if (item.autoEnter) { submitButton.click(); } } else { console.error(`找不到名稱為 ${name} 的元素`); } }; // 創建主按鈕 const createMainButton = (buttonText) => { const mainButton = document.createElement("button"); mainButton.innerText = buttonText; mainButton.classList.add(styles.mainButton); mainButton.style.width = "86%"; return mainButton; }; // 創建設定按鈕 const createSettingButton = () => { const settingButton = document.createElement("button"); settingButton.innerText = "⚙️"; settingButton.classList.add(styles.settingButton); settingButton.style.width = "14%"; settingButton.id = "settingButton"; return settingButton; }; // 創建選項 const createMenuItem = (element, customize) => { const menuItem = document.createElement("button"); menuItem.innerText = element.name; menuItem.id = element.name; menuItem.classList.add(styles.menuButton); menuItem.addEventListener("click", (event) => { insertCustomize(customize, event.target.id); }); return menuItem; }; // 創建選單(包含多個選項) const createMenu = (containerNode, customize) => { const menu = document.createElement("div"); menu.id = "helper_menu"; menu.classList.add(styles.menu); menu.style.display = "none"; menu.style.width = `${containerNode.offsetWidth}px`; customize.forEach((element) => { const menuItem = createMenuItem(element, customize); menu.appendChild(menuItem); }); return menu; }; const bindElementContainer = (elements, containerClass) => { const container = document.createElement("div"); if (containerClass) { container.classList.add(containerClass); } elements.forEach((element) => { container.appendChild(element); }); return container; }; // addMenuBtn 函數用於新增包含主按鈕和設定按鈕的選單按鈕 function addMenuBtnWrapper(containerNode, customize, buttonText = "Click Me" // 主按鈕的文字,預設值為 "Click Me" ) { // 創建主按鈕和設定按鈕 const mainButton = createMainButton(buttonText); const settingButton = createSettingButton(); // 將主按鈕和設定按鈕組合在一個容器中 const assButton = bindElementContainer([settingButton, mainButton], config.CONTAINER_CLASS); // 根據客製化選單項目創建選單 const menu = createMenu(containerNode, customize); // 當滑鼠移到按鈕上時,顯示選單 assButton.addEventListener("mouseenter", () => { menu.style.display = "block"; }); // 創建按鈕包裹器,並將組合按鈕和選單加入其中 const buttonWrapper = document.createElement("div"); buttonWrapper.style.width = `${containerNode.offsetWidth}px`; buttonWrapper.appendChild(assButton); buttonWrapper.appendChild(menu); // 將按鈕包裹器加入到容器節點中 containerNode.appendChild(buttonWrapper); // 當滑鼠離開按鈕包裹器時,隱藏選單 buttonWrapper.addEventListener("mouseleave", () => { setTimeout(() => { menu.style.display = "none"; }, 300); }); console.log("已新增按鈕"); } function setCustomizeBtn(customize) { // 找到 settingButton 元素 const settingButton = document.getElementById('settingButton'); let newPosition; let newAutoEnter; // 當點擊 settingButton 時觸發事件 settingButton.addEventListener('click', () => { // 創建彈出視窗 const popup = document.createElement('div'); popup.style.position = 'fixed'; popup.style.top = '50%'; popup.style.left = '50%'; popup.style.transform = 'translate(-50%, -50%)'; popup.style.background = '#525467'; popup.style.border = '1px solid black'; popup.style.padding = '30px'; popup.style.width = '80%'; popup.style.maxWidth = '800px'; popup.style.height = '60%'; popup.style.maxHeight = '1200px'; popup.style.zIndex = '9999'; // 創建新增按鈕 const addButton = document.createElement('button'); addButton.textContent = '新增(add)'; addButton.style.margin = '10px'; addButton.style.border = '2px solid #ffffff'; addButton.addEventListener('click', () => { // 新增一個 item const newItem = { name: '', position: '', content: '' }; customize.push(newItem); renderTable(); }); popup.appendChild(addButton); // 創建編輯按鈕 const editButton = document.createElement('button'); editButton.textContent = '編輯(edit)'; editButton.style.margin = '10px'; editButton.style.border = '2px solid #ffffff'; editButton.addEventListener('click', () => { // 編輯一個 item const index = prompt('請輸入要編輯的編號(edit index)'); if (index && Number(index) >= 1 && index <= customize.length) { const item = customize[Number(index) - 1]; // 編輯 name const newName = prompt('請輸入新的 name', item.name); if (newName !== null) { item.name = newName; } // 編輯 position do { newPosition = prompt('請輸入新的 position (只能輸入 start 或 end)', item.position); } while (newPosition !== null && newPosition !== 'start' && newPosition !== 'end'); if (newPosition !== null) { item.position = newPosition; } // 編輯 position do { newAutoEnter = prompt('請輸入新的 AutoEnter (只能輸入 y 或 n)', item.autoEnter ? 'y' : 'n'); } while (newAutoEnter !== null && newAutoEnter !== 'y' && newAutoEnter !== 'n'); if (newAutoEnter !== null) { if (newAutoEnter === 'y') { item.autoEnter = true; } else { item.autoEnter = false; } } // 編輯 content // const textarea = document.createElement('textarea'); // textarea.value = item.content; // textarea.style.width = '100%'; // textarea.style.height = '100px'; const newContent = prompt('請輸入新的 content', item.content); if (newContent !== null) { item.content = newContent; } // 重新渲染表格 renderTable(); } else { alert('輸入的編號不合法'); } }); popup.appendChild(editButton); // 創建刪除按鈕 const deleteButton = document.createElement('button'); deleteButton.textContent = '刪除(delete)'; deleteButton.style.margin = '10px'; deleteButton.style.border = '2px solid #ffffff'; deleteButton.addEventListener('click', () => { // 刪除一個 item const index = prompt('請輸入要刪除的編號(delete index)'); if (index && Number(index) >= 1 && index <= customize.length) { customize.splice(Number(index) - 1, 1); renderTable(); } else { alert('輸入的編號不合法 (invalid index)'); } }); popup.appendChild(deleteButton); // 創建關閉按鈕 const closeButton = document.createElement('button'); closeButton.textContent = '儲存並離開(save&exit)'; closeButton.style.position = 'absolute'; closeButton.style.top = '5px'; closeButton.style.right = '5px'; closeButton.addEventListener('click', () => { console.log(customize); // 儲存修改後的 customize 資料 GM_setValue('customizeData', customize); // // 重寫一次 helper_menu // const helper_menu = document.getElementById('helper_menu'); // const menu = createMenu(helper_menu); // helper_menu.replaceWith(menu); // 上面的做不出來 // 所以只好重新整理頁面 location.reload(); document.body.removeChild(popup); }); popup.appendChild(closeButton); // 創建表格 const table = document.createElement('table'); popup.appendChild(table); // 創建表頭 const thead = document.createElement('thead'); const tr = document.createElement('tr'); const th1 = document.createElement('th'); const th2 = document.createElement('th'); const th3 = document.createElement('th'); const th4 = document.createElement('th'); const th5 = document.createElement('th'); th1.textContent = '編號(index)'; th2.textContent = '名稱(name)'; th3.textContent = '位置(position)'; th4.textContent = '自動輸入(autoEnter)?'; th5.textContent = '內容(content)'; tr.appendChild(th1); tr.appendChild(th2); tr.appendChild(th3); tr.appendChild(th4); tr.appendChild(th5); thead.appendChild(tr); table.appendChild(thead); // 創建表身 const tbody = document.createElement('tbody'); table.appendChild(tbody); // 渲染表格 function renderTable() { // 先清空表格內容 tbody.innerHTML = ''; // 重新渲染表格 customize.forEach((item, index) => { const tr = document.createElement('tr'); const td1 = document.createElement('td'); const td2 = document.createElement('td'); const td3 = document.createElement('td'); const td4 = document.createElement('td'); const td5 = document.createElement('td'); td1.textContent = index + 1; td2.textContent = item.name; td3.textContent = item.position; td4.textContent = item.autoEnter; td5.textContent = item.content; tr.appendChild(td1); tr.appendChild(td2); tr.appendChild(td3); tr.appendChild(td4); tr.appendChild(td5); tbody.appendChild(tr); }); } // 渲染初始表格 renderTable(); // 點擊彈窗外的地方關閉彈窗 popup.addEventListener('click', (event) => { if (event.target === popup) { document.body.removeChild(popup); } }); // 將彈出視窗加入頁面中 document.body.appendChild(popup); }); } var css_248z = ".custom-element{background-color:#f9f9f9;border:1px solid #ccc;border-radius:4px;color:#333;font-size:14px;padding:10px}"; styleInject(css_248z); main(); function main() { // 頁面載入完成後執行 onloadSafe(() => { // 監聽 nav 元素 console.log("=====監聽 nav 元素====="); // 定義常用咒文 let customize; sentinel.on("nav", (nav) => { console.log("===== trigger sentinel.on nav ====="); // 讀取 customize 設定 let GM_customize = GM_getValue("customizeData", customize); // 如果 user 已經有設定了就用 user 的,沒有就用預設值 if (GM_customize) { customize = GM_customize; } else { customize = config.init_customize; GM_setValue("customizeData", customize); } //找不到就新增 const container = document.getElementById("helper_menu"); if (!container) { // 獲得目標元素 const aimsNode = document.querySelector(config.NAV_MENU); // 新增一個容器 const container = document.createElement("div"); container.classList.add(config.CONTAINER_CLASS); container.id = "helper_menu"; if (aimsNode) { // 設定 container 寬度為父元素寬度 container.style.width = `${aimsNode.offsetWidth}px`; // 設定 container 寬度為父元素寬度 // 將容器元素插入到目標元素後面 aimsNode.parentNode?.insertBefore(container, aimsNode.nextSibling); // 新增一個按鈕元素 addMenuBtnWrapper(container, customize, config.HELPER_MENU_TEXT); // 設定 "設定按鈕"的點擊事件 setCustomizeBtn(customize); } } }); }); } }));