// ==UserScript== // @name AI 提示词大师 Pro // @namespace http://tampermonkey.net/ // @version 10.0.3 // @license MIT // @description 全能整合版:在标题栏直观显示“存储模式”和“填充模式”。支持增量导入、分类折叠、Gemini 完美适配。新增面板折叠功能与UI自适应。 // @author WaterHuo // @match *://gemini.google.com/* // @match *://chatgpt.com/* // @match *://claude.ai/* // @match *://chat.deepseek.com/* // @match *://www.doubao.com/* // @match *://www.kimi.ai/* // @match *://www.kimi.com/* // @match *://kimi.moonshot.cn/* // @match *://grok.com/* // @match *://x.com/i/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_xmlhttpRequest // @connect tfntmhg1.api.lncldglobal.com // @run-at document-start // @downloadURL none // ==/UserScript== (function() { 'use strict'; // ======= 0. 配置与工具函数 ======= const CONFIG = { LC_ID: 'TFNtmHG1nSgEMcCEjz7HNGka-MdYXbMMI', LC_KEY: '8J1H0edw3g8BPJaTLDgS4UDm', API_URL: 'https://tfntmhg1.api.lncldglobal.com/1.1/classes/PromptData' }; // 获取/生成用户唯一标识 const getUserId = () => { let uid = GM_getValue('pm_uid'); if (!uid) { uid = 'user_' + Math.random().toString(36).substr(2, 9) + Date.now().toString(36); GM_setValue('pm_uid', uid); } return uid; }; const USER_ID = getUserId(); // TrustedHTML 安全策略(适配Gemini) let ttPolicy; if (window.trustedTypes && window.trustedTypes.createPolicy) { try { const policyName = 'pm-policy-' + Math.random().toString(36).substring(7); ttPolicy = window.trustedTypes.createPolicy(policyName, { createHTML: (string) => string, createScript: (string) => string }); } catch (e) { console.warn("PromptMaster: TrustedTypes policy already exists"); } } const setHTML = (el, html) => { if (!el) return; el.innerHTML = ttPolicy ? ttPolicy.createHTML(html) : html; }; // ======= 1. 数据管理核心 ======= const STORAGE_KEY = 'pm_data_v16'; const MODE_KEY = 'pm_storage_mode'; const APPEND_KEY = 'pm_append_mode'; // 持久化追加模式 const CLOUD_OBJ_ID_KEY = 'pm_cloud_oid'; const FOLD_KEY = 'pm_folded_cats'; const MINIMIZED_KEY = 'pm_is_minimized'; let currentMode = GM_getValue(MODE_KEY, 'local'); // 默认本地存储 let promptData = {}; let foldedCats = GM_getValue(FOLD_KEY, []); let isEditMode = false; let appendMode = GM_getValue(APPEND_KEY, true); // 默认追加模式 let isMinimized = GM_getValue(MINIMIZED_KEY, false); let isLoading = false; // 默认初始数据 const defaultData = { "写作类": [{ name: "📝 深度润色", content: "请优化以下文本,梳理逻辑脉络,提升语言表达的简洁性和流畅性,保留核心信息。" }], "代码类": [{ name: "💻 逻辑审查", content: "请检查这段代码的语法错误、逻辑漏洞,给出优化建议和修复方案。" }] }; // 云端API封装 const CloudAPI = { headers: { "X-LC-Id": CONFIG.LC_ID, "X-LC-Key": CONFIG.LC_KEY, "Content-Type": "application/json" }, request(method, endpoint, data = null) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: method, url: endpoint.startsWith('http') ? endpoint : CONFIG.API_URL + endpoint, headers: this.headers, data: data ? JSON.stringify(data) : null, onload: (res) => { if (res.status >= 200 && res.status < 300) { resolve(JSON.parse(res.responseText)); } else { reject(JSON.parse(res.responseText)); } }, onerror: reject }); }); }, // 获取云端数据 async fetchData() { const res = await this.request('GET', `?where={"uid":"${USER_ID}"}`); if (res.results && res.results.length > 0) { GM_setValue(CLOUD_OBJ_ID_KEY, res.results[0].objectId); return res.results[0].data; } return null; }, // 保存数据到云端 async saveData(data) { const oid = GM_getValue(CLOUD_OBJ_ID_KEY); const payload = { uid: USER_ID, data: data }; if (oid) { await this.request('PUT', `/${oid}`, payload); } else { const res = await this.request('POST', '', payload); GM_setValue(CLOUD_OBJ_ID_KEY, res.objectId); } } }; // 统一数据管理器(本地/云端切换) const DataManager = { async load() { isLoading = true; renderUI(); try { if (currentMode === 'local') { promptData = GM_getValue(STORAGE_KEY) || defaultData; } else { const cloudData = await CloudAPI.fetchData(); promptData = cloudData || defaultData; } } catch (e) { toast(`加载失败: ${e.error || '网络错误'}`); promptData = defaultData; } finally { isLoading = false; renderUI(); } }, async save() { renderUI(); try { if (currentMode === 'local') { GM_setValue(STORAGE_KEY, promptData); toast("✅ 本地已保存"); } else { toast("☁️ 正在同步云端...", 5000); await CloudAPI.saveData(promptData); toast("✅ 云端同步完成"); } } catch (e) { toast("❌ 保存失败"); console.error(e); } } }; // ======= 2. 导入导出工具 ======= const IOTools = { // 导出JSON备份 exportJSON() { const fileName = `prompt_master_${currentMode}_${new Date().toISOString().slice(0,10)}.json`; const blob = new Blob([JSON.stringify(promptData, null, 2)], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); toast("✅ 已导出备份"); }, // 增量导入JSON importJSON() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.style.display = 'none'; document.body.appendChild(input); input.onchange = (e) => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (event) => { try { const newData = JSON.parse(event.target.result); if (typeof newData === 'object' && newData !== null) { let addedCount = 0; Object.keys(newData).forEach(cat => { if (!promptData[cat]) { promptData[cat] = newData[cat]; addedCount += newData[cat].length; } else { const existingNames = new Set(promptData[cat].map(item => item.name)); newData[cat].forEach(newItem => { if (!existingNames.has(newItem.name)) { promptData[cat].push(newItem); addedCount++; } }); } }); if (addedCount > 0) { DataManager.save(); alert(`✅ 增量导入成功!\n共新增 ${addedCount} 条模板。`); } else { alert("⚠️ 导入完成,没有新增内容。"); } } else { toast("❌ 导入格式错误"); } } catch (err) { toast("❌ 导入解析失败"); } document.body.removeChild(input); }; reader.readAsText(file); }; input.click(); } }; // ======= 3. 样式表 (状态栏增强 + 半透明预览 + 自适应布局) ======= GM_addStyle(` #pm-root { font-family: -apple-system, system-ui, sans-serif; } /* 主面板样式 - 改为自适应宽度 */ .pm-panel { position: fixed; top: 80px; right: 20px; width: auto; /* 允许宽度自动 */ min-width: 260px; /* 设置一个稍微宽一点的最小值 */ max-width: 350px; /* 防止过宽 */ background: #fff; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.1); z-index: 2147483647; border: 1px solid #eee; display: flex; flex-direction: column; transition: width 0.2s ease; /* 平滑过渡 */ } /* 折叠悬浮球 */ .pm-float-ball { position: fixed; top: 80px; right: 20px; width: 40px; height: 40px; background: #1a73e8; border-radius: 50%; box-shadow: 0 4px 12px rgba(26,115,232,0.3); z-index: 2147483647; cursor: pointer; display: flex; justify-content: center; align-items: center; color: #fff; font-weight: bold; font-size: 14px; border: 2px solid #fff; transition: transform 0.2s, background 0.2s; user-select: none; } .pm-float-ball:hover { transform: scale(1.1); background: #1557b0; } /* 标题栏布局 - 增加间距和防挤压 */ .pm-header { padding: 10px 12px; background: #fcfcfc; border-bottom: 1px solid #f0f0f0; display: flex; justify-content: space-between; align-items: center; border-radius: 12px 12px 0 0; flex-wrap: wrap; /* 允许必要时换行 */ gap: 8px; /* 元素间增加间距 */ } .pm-title-area { display: flex; align-items: center; gap: 6px; /* 增加标题和左侧徽章的间距 */ flex-wrap: nowrap; flex: 1 1 auto; } .pm-title { font-size: 14px; /* 稍微加大标题字号 */ font-weight: 700; color: #1a73e8; white-space: nowrap; } /* 状态徽章 */ .pm-badge { font-size: 11px; /* 优化字体大小 */ padding: 3px 6px; border-radius: 4px; cursor: pointer; display: flex; align-items: center; transition: 0.2s; border: 1px solid transparent; user-select: none; white-space: nowrap; /* 防止徽章文字换行 */ line-height: 1.2; } .pm-badge:hover { filter: brightness(0.95); transform: translateY(-1px); } /* 存储模式配色 */ .mode-local { background: #e6f4ea; color: #137333; border-color: #ceead6; } .mode-cloud { background: #e8f0fe; color: #1967d2; border-color: #d2e3fc; } /* 填充模式配色 */ .fill-append { background: #f5f9ff; color: #406599; border-color: #e1eafc; } .fill-replace { background: #f8f0f5; color: #8b5cf6; border-color: #f3e8ff; } /* 标题栏右侧按钮区 */ .pm-header-right { display: flex; align-items: center; gap: 4px; /* 按钮间距 */ flex: 0 0 auto; /* 保持不被压缩 */ } .pm-icon-btn { padding: 5px; border-radius: 4px; cursor: pointer; font-size: 14px; display: flex; align-items: center; justify-content: center; transition: 0.2s; color: #5f6368; } .pm-icon-btn:hover { background: #f1f3f4; color: #1a73e8; } .pm-icon-active { color: #1a73e8; font-weight:bold; background: #e8f0fe; } /* 内容区域 */ .pm-body { padding: 8px; max-height: 60vh; overflow-y: auto; scrollbar-width: thin; position: relative; min-height: 100px; } /* 底部工具栏 */ .pm-footer { padding: 8px; border-top: 1px solid #eee; display: flex; gap: 6px; background: #fff; border-radius: 0 0 12px 12px; } .pm-tool-btn { flex: 1; padding: 6px; border: 1px solid #f1f3f4; background: #f8f9fa; border-radius: 6px; font-size: 11px; cursor: pointer; color: #5f6368; text-align: center; transition:0.2s; white-space: nowrap; } .pm-tool-btn:hover { background: #e8f0fe; color: #1967d2; border-color: #d2e3fc; } /* 分类与模板列表 */ .pm-cat-wrap { margin-bottom: 8px; border-radius: 8px; overflow: hidden; } .pm-cat-header { display: flex; align-items: center; padding: 6px 4px; background: #f8f9fa; cursor: pointer; position: relative; } .pm-cat-fold-icon { font-size: 10px; margin-right: 6px; transition: transform 0.2s; color: #70757a; } .pm-cat-name { font-size: 12px; /* 字体微调 */ color: #5f6368; font-weight: 700; flex: 1; text-transform: uppercase; letter-spacing: 0.5px; } .pm-cat-tools { display: none; gap: 6px; margin-right: 4px; } .pm-cat-header:hover .pm-cat-tools { display: flex; } .pm-tpl-list { padding: 4px 0; } .pm-tpl-list.folded { display: none; } .pm-item-wrap { position: relative; margin-bottom: 2px; } .pm-btn { width: 100%; border: none; background: transparent; padding: 8px 10px; text-align: left; font-size: 13px; /* 模板字体加大 */ border-radius: 6px; cursor: pointer; color: #3c4043; transition: 0.2s; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .pm-btn:hover { background: #f1f3f4; color: #1a73e8; } /* 半透明预览浮窗 */ #pm-preview-float { position: fixed; display: none; width: auto; max-width: 280px; background: rgba(255, 255, 255, 0.85); backdrop-filter: blur(8px); border: 1px solid rgba(0,0,0,0.08); box-shadow: 0 4px 16px rgba(0,0,0,0.1); border-radius: 8px; padding: 10px; font-size: 12px; line-height: 1.5; color: #444; z-index: 2147483647; pointer-events: auto; word-break: break-all; transition: opacity 0.2s; opacity: 0; } #pm-preview-float.show { opacity: 1; /* 显示时不透明 */ } /* 原地编辑器 */ .pm-inline-editor { background: #fff; border: 1px solid #1a73e8; border-radius: 8px; padding: 8px; margin: 4px 0; box-shadow: 0 4px 12px rgba(26,115,232,0.15); } .pm-inline-editor textarea { width: 100%; height: 100px; border: 1px solid #dadce0; border-radius: 4px; padding: 6px; font-size: 12px; box-sizing: border-box; resize: vertical; margin: 5px 0; } .pm-inline-editor input, .pm-inline-editor select { width: 100%; border: 1px solid #dadce0; border-radius: 4px; padding: 4px 6px; font-size: 12px; box-sizing: border-box; margin-bottom: 4px; } .pm-ed-btns { display: flex; justify-content: flex-end; gap: 6px; } .pm-ebtn { padding: 3px 8px; font-size: 11px; border-radius: 4px; cursor: pointer; border: none; } .pm-save { background: #1a73e8; color: #fff; } .pm-cancel { background: #f1f3f4; color: #5f6368; } /* 加载中遮罩 */ .pm-loading { position: absolute; top:0; left:0; width:100%; height:100%; background:rgba(255,255,255,0.8); display:flex; justify-content:center; align-items:center; font-size:12px; color:#666; z-index:10; } /* 提示弹窗 */ .pm-toast { position: fixed; bottom: 30px; left: 50%; transform: translateX(-50%); background: #323232; color: #fff; padding: 6px 16px; border-radius: 16px; font-size: 12px; z-index: 2147483647; display: none; } `); // ======= 4. 稳定填充核心 ======= async function stableInject(text) { // 兼容多平台输入框选择器 const inputField = document.querySelector( 'div[role="textbox"], #prompt-textarea, textarea[placeholder*="输入"], #chat-input, [contenteditable="true"], textarea' ); if (!inputField) return toast("❌ 未找到输入框"); inputField.focus(); const isRich = inputField.isContentEditable; const oldVal = isRich ? inputField.innerText : inputField.value; const newVal = (appendMode && oldVal.trim()) ? (oldVal + "\n" + text) : text; try { if (isRich) { // 富文本输入框处理 const selection = window.getSelection(); const range = document.createRange(); range.selectNodeContents(inputField); selection.removeAllRanges(); selection.addRange(range); document.execCommand('delete', false); document.execCommand('insertText', false, newVal); } else { // 普通文本框处理(绕过输入限制) const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set; setter.call(inputField, newVal); inputField.dispatchEvent(new Event('input', { bubbles: true })); } toast("✅ 填充成功"); inputField.focus(); // 滚动到输入框底部 setTimeout(() => { inputField.scrollTop = inputField.scrollHeight; }, 10); } catch (e) { toast("❌ 尝试填充失败"); } } // ======= 5. UI 渲染引擎 ======= function toast(msg, time=2000) { const t = document.getElementById('pm-toast') || (() => { const d = document.createElement('div'); d.id = 'pm-toast'; d.className = 'pm-toast'; document.body.appendChild(d); return d; })(); setHTML(t, msg); t.style.display = 'block'; setTimeout(() => t.style.display = 'none', time); } function renderUI() { if (!document.body) return; // 创建根容器和预览浮窗 let root = document.getElementById('pm-root'); if (!root) { root = document.createElement('div'); root.id = 'pm-root'; document.body.appendChild(root); const pf = document.createElement('div'); pf.id = 'pm-preview-float'; document.body.appendChild(pf); } const previewFloat = document.getElementById('pm-preview-float'); // 如果处于折叠状态,只渲染悬浮球 if (isMinimized) { setHTML(root, `
AI
`); document.getElementById('pm-restore-btn').onclick = () => { isMinimized = false; GM_setValue(MINIMIZED_KEY, false); renderUI(); }; return; } // 样式逻辑(动态生成状态徽章类名/文本) const modeClass = currentMode === 'local' ? 'mode-local' : 'mode-cloud'; const modeText = currentMode === 'local' ? '🏠 本地' : '☁️ 云端'; const fillClass = appendMode ? 'fill-append' : 'fill-replace'; const fillText = appendMode ? '➕ 追加' : '🔄 替换'; const configIcon = isEditMode ? '✅' : '⚙️'; // 渲染主面板 setHTML(root, `
提示词大师 ${modeText}
${fillText} ${configIcon}
${isLoading ? '
同步中...
' : ''}
`); // 绑定折叠按钮事件 document.getElementById('pm-fold-btn').onclick = () => { isMinimized = true; GM_setValue(MINIMIZED_KEY, true); renderUI(); }; if (isLoading) return; // 渲染分类和模板列表 const container = document.getElementById('pm-list-container'); Object.keys(promptData).forEach(cat => { const isFolded = foldedCats.includes(cat); const catWrap = document.createElement('div'); catWrap.className = 'pm-cat-wrap'; // 分类头部(折叠/编辑) const header = document.createElement('div'); header.className = 'pm-cat-header'; setHTML(header, ` ${cat} ${isEditMode ? `
✏️ ×
` : ''} `); // 分类折叠/展开逻辑 header.onclick = (e) => { if (e.target.closest('.pm-cat-tools')) return; if (isFolded) { foldedCats = foldedCats.filter(c => c !== cat); } else { foldedCats.push(cat); } GM_setValue(FOLD_KEY, foldedCats); renderUI(); }; // 分类编辑/删除事件 if (isEditMode) { header.querySelector('.pm-ed-cat').onclick = () => editCatName(catWrap, cat); header.querySelector('.pm-del-cat').onclick = () => deleteCat(cat); } catWrap.appendChild(header); // 模板列表 const tplList = document.createElement('div'); tplList.className = `pm-tpl-list ${isFolded ? 'folded' : ''}`; // 渲染单个模板 promptData[cat].forEach((item, idx) => { const itemWrap = document.createElement('div'); itemWrap.className = 'pm-item-wrap'; const btn = document.createElement('button'); btn.className = 'pm-btn'; btn.innerText = item.name; // 预览浮窗逻辑 btn.onmouseenter = (e) => { if (isEditMode) return; const rect = btn.getBoundingClientRect(); // 显示模板名称 + 150字预览 const nameText = `
${item.name}
`; const coreText = item.content.length > 150 ? item.content.substring(0, 150) + "..." : item.content; setHTML(previewFloat, `${nameText}${coreText.replace(/\n/g, '
')}`); // 浮窗显示+动画 previewFloat.style.display = 'block'; previewFloat.classList.add('show'); // 优化浮窗位置(避免出界) let topPos = rect.top + window.scrollY; let leftPos = rect.right + 15; // 右侧出界则显示在左侧 if (leftPos + previewFloat.offsetWidth > window.innerWidth) { leftPos = rect.left - previewFloat.offsetWidth - 15; } // 底部出界则向上调整 if (topPos + previewFloat.offsetHeight > window.innerHeight + window.scrollY) { topPos = rect.bottom - previewFloat.offsetHeight + window.scrollY; } previewFloat.style.top = `${topPos}px`; previewFloat.style.left = `${leftPos}px`; previewFloat.style.right = 'auto'; }; // 鼠标离开隐藏浮窗(带过渡动画) btn.onmouseleave = () => { previewFloat.classList.remove('show'); setTimeout(() => { previewFloat.style.display = 'none'; }, 150); }; // 模板点击事件(编辑/填充) btn.onclick = () => { if (isEditMode) { editTpl(itemWrap, cat, idx); } else { stableInject(item.content); previewFloat.style.display = 'none'; } }; itemWrap.appendChild(btn); tplList.appendChild(itemWrap); }); // 编辑模式下添加新模板按钮 if (isEditMode) { const addTplBtn = document.createElement('button'); addTplBtn.className = 'pm-btn'; addTplBtn.style.border = "1px dashed #ccc"; addTplBtn.innerText = "+ 新模板"; addTplBtn.onclick = () => editTpl(tplList, cat, -1, addTplBtn); tplList.appendChild(addTplBtn); } catWrap.appendChild(tplList); container.appendChild(catWrap); }); // 事件绑定 // 编辑模式切换 document.getElementById('pm-config-btn').onclick = () => { isEditMode = !isEditMode; renderUI(); }; // 填充模式切换(持久化) document.getElementById('pm-switch-fill').onclick = () => { appendMode = !appendMode; GM_setValue('pm_append_mode', appendMode); renderUI(); }; // 存储模式切换 document.getElementById('pm-switch-storage').onclick = async () => { if (isLoading) return; const targetMode = currentMode === 'local' ? '☁️ 云端' : '🏠 本地'; if (confirm(`确认切换至 [${targetMode}] 模式?`)) { currentMode = currentMode === 'local' ? 'cloud' : 'local'; GM_setValue(MODE_KEY, currentMode); await DataManager.load(); } }; // 导入/导出事件 document.getElementById('pm-export-btn').onclick = IOTools.exportJSON; document.getElementById('pm-import-btn').onclick = IOTools.importJSON; // 新建分类事件 if (isEditMode) { document.getElementById('pm-new-cat').onclick = () => { const n = prompt("请输入新分类名称:"); if (n) { promptData[n] = []; DataManager.save(); } }; } } // ======= 6. 辅助编辑逻辑 ======= // 编辑分类名称 function editCatName(wrap, oldName) { const header = wrap.querySelector('.pm-cat-header'); header.style.display = 'none'; const editor = document.createElement('div'); editor.className = 'pm-inline-editor'; setHTML(editor, `
`); wrap.prepend(editor); editor.querySelector('.pm-cancel').onclick = () => renderUI(); editor.querySelector('.pm-save').onclick = () => { const n = editor.querySelector('#new-cat-inp').value.trim(); if (n && n !== oldName) { promptData[n] = promptData[oldName]; delete promptData[oldName]; DataManager.save(); } else { renderUI(); } }; } // 删除分类 function deleteCat(catName) { if (!confirm(`确定删除分类 [${catName}] 吗?删除后该分类下的所有模板也会被移除。`)) return; delete promptData[catName]; DataManager.save(); } // 编辑/新增模板 function editTpl(container, cat, idx, addBtn = null) { const isNew = idx === -1; const item = isNew ? { name: "", content: "" } : promptData[cat][idx]; const editor = document.createElement('div'); editor.className = 'pm-inline-editor'; // 生成分类下拉选项 let cats = Object.keys(promptData).map(c => `` ).join(''); setHTML(editor, `
`); // 隐藏原模板按钮/新增按钮 if (!isNew) container.querySelector('.pm-btn').style.display = 'none'; if (addBtn) addBtn.style.display = 'none'; container.appendChild(editor); // 取消编辑 editor.querySelector('.pm-cancel').onclick = () => renderUI(); // 保存模板 editor.querySelector('.pm-save').onclick = () => { const n = editor.querySelector('#ed-name').value.trim(); const c = editor.querySelector('#ed-cont').value; const tCat = editor.querySelector('#ed-cat').value; if (!n || !c) { toast("❌ 名称和内容不能为空"); return; } // 编辑模式:删除原模板 if (!isNew) promptData[cat].splice(idx, 1); // 添加新模板 promptData[tCat].push({ name: n, content: c }); DataManager.save(); }; } // ======= 7. 启动初始化 ======= const init = async () => { if (!document.getElementById('pm-root')) { renderUI(); await DataManager.load(); } }; // 监听页面加载,确保UI正确渲染 const observer = new MutationObserver(() => { if (!document.getElementById('pm-root')) renderUI(); }); const startObserver = () => { if (document.body) { observer.observe(document.body, { childList: true, subtree: false }); init(); } else { setTimeout(startObserver, 100); } }; startObserver(); // 兜底检查(防止Observer失效) setInterval(() => { if (document.body && !document.getElementById('pm-root')) renderUI(); }, 2000); })();