// ==UserScript==
// @name PromptHelper
// @namespace http://tampermonkey.net/
// @version 1.7.5
// @description PromptHelper:通用于 ChatGPT, Gemini, Claude, Kimi, DeepSeek, 通义、元宝、Google AI Studio、Grok、豆包 的侧边模板助手;主/设分离;导入/导出;从聊天栏读取并回填;Kimi/Claude 专项处理(覆盖、不重复、换行保真)。新增:站点默认模板(通配符、早保存优先);“应用默认模板”一键套用站点默认/全局默认;修复并发覆盖(读-改-写);Helper 按钮改蓝色以适配黑底站点。—— 本版:新增夜间模式(黑色系 UI),一键切换并持久化记忆;Claude 换行保真策略保持。—— 改进版:导入/导出增强(同名标准化、冲突策略、可选跳过重复内容、可选整包导入导出、schema/version 兼容、容错更清晰);设置页标题改为“设置站点默认模板”;导出不再询问是否包含默认模板且默认不导出默认模板。;更新默认模板。
// @author Sauterne
// @match http://chat.openai.com/*
// @match https://chat.openai.com/*
// @match http://chatgpt.com/*
// @match https://chatgpt.com/*
// @match http://gemini.google.com/*
// @match https://gemini.google.com/*
// @match http://claude.ai/*
// @match https://claude.ai/*
// @match http://demo.fuclaude.com/*
// @match https://demo.fuclaude.com/*
// @match http://www.kimi.com/*
// @match https://www.kimi.com/*
// @match http://kimi.com/*
// @match https://kimi.com/*
// @match http://kimi.moonshot.cn/*
// @match https://kimi.moonshot.cn/*
// @match http://chat.deepseek.com/*
// @match https://chat.deepseek.com/*
// @match http://www.tongyi.com/*
// @match https://www.tongyi.com/*
// @match http://yuanbao.tencent.com/chat/*
// @match https://yuanbao.tencent.com/chat/*
// @match http://aistudio.google.com/*
// @match https://aistudio.google.com/*
// @match http://grok.com/*
// @match https://grok.com/*
// @match http://www.grok.com/*
// @match https://www.grok.com/*
// @match http://doubao.com/*
// @match https://doubao.com/*
// @match http://www.doubao.com/*
// @match https://www.doubao.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @run-at document-start
// @license MIT
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
const SETTINGS = { forceOpenShadow: true };
const UI_STORE_KEY = 'universal_prompt_helper_ui_settings';
const PROMPTS_STORE_KEY = 'universal_prompt_helper_prompts';
const LANG_STORE_KEY = 'universal_prompt_helper_lang';
const SITE_DEFAULTS_STORE_KEY = 'universal_prompt_helper_site_defaults';
const THEME_STORE_KEY = 'universal_prompt_helper_theme';
const DEFAULT_UI = { top: 100, toggleWidth: 120, toggleHeight: 40 };
const DEFAULT_TEMPLATE_ID = 'default_interactive';
// 导入/导出相关:保持原 schema,同时新增 bundle schema(可选)
const EXPORT_SCHEMA = 'prompthelper.templates.v1';
const EXPORT_BUNDLE_SCHEMA = 'prompthelper.bundle.v1';
const IMPORT_SUFFIX_BASE = ' (imported)';
function loadUISettings(){
try{
const saved = GM_getValue(UI_STORE_KEY, null);
const parsed = saved ? JSON.parse(saved) : {};
return { ...DEFAULT_UI, ...(parsed || {}) };
}catch{ return { ...DEFAULT_UI }; }
}
function saveUISettings(s){ GM_setValue(UI_STORE_KEY, JSON.stringify(s)); }
function loadSiteDefaults(){
try{
const saved = GM_getValue(SITE_DEFAULTS_STORE_KEY, '[]');
const arr = JSON.parse(saved);
return Array.isArray(arr) ? arr : [];
}catch{ return []; }
}
function saveSiteDefaults(arr){
GM_setValue(SITE_DEFAULTS_STORE_KEY, JSON.stringify(Array.isArray(arr)?arr:[]));
}
function loadTheme(){
const t = GM_getValue(THEME_STORE_KEY, 'light');
return (t==='dark') ? 'dark' : 'light';
}
function saveTheme(t){
GM_setValue(THEME_STORE_KEY, (t==='dark')?'dark':'light');
}
// —— 新增:名称标准化/哈希/冲突辅助 —— //
function normalizeName(name){ return String(name||'').trim(); }
function normalizeKey(name){ return normalizeName(name).toLowerCase(); }
function djb2Hash(str){
str = String(str||'');
let hash = 5381;
for(let i=0;i ${inner}
');paras.push(`
${escapeHtml(ln)}
`; } return html; } function pasteIntoProseMirror(editableEl, plainText, opts={}){ const pmStrict = !!opts.pmStrict; const html = pmStrict ? textToProseMirrorParagraphHTML(plainText) : textToHtmlPreserveBlankLines(plainText); editableEl.focus(); let ok=false; try{ const dt=new DataTransfer(); dt.setData('text/plain',plainText); dt.setData('text/html',html); const evt=new ClipboardEvent('paste',{bubbles:true,cancelable:true,clipboardData:dt}); ok=editableEl.dispatchEvent(evt); }catch(_){ok=false;} if(!ok){ try{ document.execCommand('insertHTML', false, html); } catch(_){ editableEl.textContent=''; editableEl.dispatchEvent(new Event('input',{bubbles:true})); editableEl.textContent=plainText; editableEl.dispatchEvent(new Event('input',{bubbles:true})); } } } const nativeTextareaValueSetter=Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype,'value')?.set; const nativeInputValueSetter=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set; function setNativeValue(el,value){const setter=el.tagName==='TEXTAREA'?nativeTextareaValueSetter:el.tagName==='INPUT'?nativeInputValueSetter:null; if(setter) setter.call(el,value); else el.value=value;} window.addEventListener('DOMContentLoaded', () => { const siteConfigs = { 'openai.com': { name: 'ChatGPT', inputSelector: '#prompt-textarea' }, 'chatgpt.com': { name: 'ChatGPT', inputSelector: '#prompt-textarea' }, 'gemini.google.com': { name: 'Gemini', shadowRootSelector: 'chat-app', inputSelector: 'div.initial-input-area textarea, rich-textarea .ql-editor, [contenteditable="true"][role="textbox"]' }, 'claude.ai': { name: 'Claude', inputSelector: '.ProseMirror[contenteditable="true"]' }, 'fuclaude.com': { name: 'Claude', inputSelector: '.ProseMirror[contenteditable="true"]' }, 'kimi.com': { name: 'Kimi', inputSelector: 'div.chat-input-editor[data-lexical-editor="true"], div[contenteditable="true"], textarea, [role="textbox"], [data-lexical-editor]' }, 'kimi.moonshot.cn': { name: 'Kimi', inputSelector: 'div.chat-input-editor[data-lexical-editor="true"], div[contenteditable="true"], textarea, [role="textbox"], [data-lexical-editor]' }, 'deepseek.com': { name: 'DeepSeek', inputSelector: 'textarea[placeholder*="随便聊点什么"], textarea[placeholder*="Ask me anything"], div[contenteditable="true"], #chat-input, [role="textbox"]' }, 'tongyi.com': { name: '通义', inputSelector: 'textarea[placeholder*="有问题,随时问通义"], textarea[placeholder*="问题"], textarea, div[contenteditable="true"], [role="textbox"]' }, 'yuanbao.tencent.com': { name: '腾讯元宝', inputSelector: 'textarea[placeholder*="输入问题"], textarea[placeholder*="问题"], textarea, div[contenteditable="true"], [role="textbox"]' }, 'aistudio.google.com': { name: 'Google AI Studio', shadowRootSelector: 'app-root', inputSelector: '[contenteditable="true"], textarea, [role="textbox"], [aria-label*="prompt"], [aria-label*="Prompt"], [placeholder*="prompt"], [placeholder*="Prompt"], .prompt-input, #prompt-input, input[type="text"]' }, 'grok.com': { name: 'Grok', inputSelector: 'form .query-bar textarea[aria-label], textarea[aria-label*="Grok"], textarea[aria-label*="向 Grok"], textarea' }, 'doubao.com': { name: '豆包', inputSelector: 'textarea[placeholder*="输入"], textarea[placeholder*="问题"], textarea, div[contenteditable="true"], [role="textbox"], [aria-label*="输入"], [aria-label*="提问"], [data-lexical-editor], .ProseMirror' } }; const defaultPrompts={ [DEFAULT_TEMPLATE_ID]:{ name:"通用交互式提问模板", template:`# Universal Research Assistant Protocol ## CORE IDENTITY You are a Research Intelligence System designed for absolute accuracy through systematic investigation and logical reasoning. You prioritize truth above all else and engage in intelligent dialogue to ensure perfect understanding. ## HIERARCHY OF PRINCIPLES ### Priority 1: ABSOLUTE CORRECTNESS - Never fabricate any information - Mark uncertainties explicitly with confidence levels - Require minimum 3 independent sources for critical claims - If unsure, say "I cannot verify this" rather than guess ### Priority 2: PERFECT UNDERSTANDING - Detect ambiguities, contradictions, and false premises - Clarify before proceeding with research - Confirm mutual understanding through interaction ### Priority 3: COMPREHENSIVE QUALITY - Provide complete, specific, actionable information - Depth over breadth - thorough analysis not surface coverage - Evidence-based reasoning chains fully exposed ## OPERATIONAL PROTOCOL ### PHASE 1: QUERY VALIDATION Analyze the user's question for: - Clarity and specificity - Logical consistency (e.g., reject "why 1+1≠2" premises) - Completeness of context - Feasibility with available resources IF issues detected: \`\`\` 📋 需要澄清 检测到的问题: [Specific issue] 请确认或提供: 1. [Specific clarification needed] 2. [Additional context required] 示例回答:"我想了解[clarified topic]在[specific context]中的[specific aspect]" \`\`\` WAIT for response before proceeding ### PHASE 2: DEEP ANALYSIS Internal reasoning process (can use English for accuracy): 1. Decompose to first principles 2. Generate testable hypotheses 3. Identify required evidence 4. Plan search strategy ### PHASE 3: SYSTEMATIC RESEARCH Execute searches progressively: - Core: "[topic] authoritative source" - Academic: "[topic] research study peer reviewed" - Verification: "[claim] fact check evidence" - Currency: "[topic] 2024 2025 latest" - Contradiction: "[topic] criticism problems limitations" Source evaluation: - Tier 1: Official/Academic (highest trust) - Tier 2: Established media/Experts - Tier 3: Multiple corroborating sources - Reject: Unsourced/Contradicted/Biased ### PHASE 4: COLLABORATIVE ENHANCEMENT IF critical information inaccessible: \`\`\` 🤝 需要您的协助 已验证信息: ✓ [What is confirmed] 信息缺口: ○ [What is missing] 如您能提供: - [Specific resource access needed] 当前可确认:[Partial answer] \`\`\` ### PHASE 5: SYNTHESIS & OUTPUT ## 研究报告 ### 核心发现 [2-3句关键结论] ### 验证事实 • **事实1** [置信度:95%] 来源:[Source 1], [Source 2], [Source 3] • **事实2** [置信度:90%] 来源:[Source 1], [Source 2] ### 逻辑推理 \`\`\` 前提A (已验证) + 前提B (已验证) ↓ [推理过程] 结论C [置信度:85%] \`\`\` ### ⚠️ 不确定因素 - [Uncertain element - marked clearly] - 总体置信度:[X%] ### 参考文献 1. [Complete citation with URL] 2. [Complete citation with URL] --- ## USER QUESTION {User Question} --- Execute this protocol now. Prioritize accuracy over speed. Think deeply, research thoroughly, interact intelligently.` } }; const translations={ zh:{ toggleButton:"Helper",panelTitle:"PromptHelper",collapseTitle:"收起", selectTemplate:"选择模板",newBtn:"新建", saveBtn:"保存",deleteBtn:"删除", templateName:"模板名称",templateNamePlaceholder:"为您的模板命名", templateContent:"模板内容 (使用 {User Question} 作为占位符)", copyBtn:"复制到剪贴板",copiedBtn:"已复制!", submitBtn:"应用到聊天栏", selectDefault:"-- 选择一个模板 --", alertSaveSuccess:"模板已保存!", alertSaveError:"模板名称和内容不能为空!", alertDeleteConfirm:"确定要删除模板", alertDeleteError:"请先选择一个要删除的模板!", alertCopyError:"复制失败,请查看控制台。", alertSubmitError:"未找到当前网站的输入框。", alertTemplateError:"请先选择或创建一个模板!", alertCannotDeleteDefault:"默认模板不可删除。", alertNoUserInput:"聊天栏为空,请先在聊天栏输入内容再应用模板。", settingsTitle:"基础设置", settingTop:"容器顶部偏移(px)", settingToggleWidth:"Helper 按钮宽度(px)", settingToggleHeight:"Helper 按钮高度(px)", settingsSave:"保存设置", settingsReset:"恢复默认", importBtn:"导入模板", exportBtn:"导出模板", alertExportEmpty:"没有可导出的模板(默认模板不导出)。", alertExportDone:"模板已导出为文件:", alertImportInvalid:"导入失败:文件格式无效或为空。", alertImportDone:(added,renamed)=>`成功导入 ${added} 个模板(其中 ${renamed} 个已重命名)。`, quickApplyBtn:"应用默认模板", siteDefaultsTitle:"设置站点默认模板", siteDefaultsList:"已保存规则", sitePattern:"域名/通配符", siteTemplate:"绑定模板", siteNewBtn:"新建规则", siteSaveBtn:"保存规则", siteDeleteBtn:"删除规则", alertSitePatternRequired:"请输入域名或通配符(如 *.example.com、kimi.* 等)。", alertSiteTemplateRequired:"请选择要绑定的模板。", alertSiteSelectFirst:"请先选择一条规则。", alertSiteSaved:"规则已保存!", alertSiteDeleted:"规则已删除!", themeToggleLightTitle:"切换夜间模式", themeToggleDarkTitle:"切换日间模式" }, en:{ toggleButton:"Helper",panelTitle:"PromptHelper",collapseTitle:"Collapse", selectTemplate:"Select Template",newBtn:"New", saveBtn:"Save",deleteBtn:"Delete", templateName:"Template Name",templateNamePlaceholder:"Name your template", templateContent:"Template Content (use {User Question} as placeholder)", copyBtn:"Copy to Clipboard",copiedBtn:"Copied!", submitBtn:"Apply to Chat Box", selectDefault:"-- Select a template --", alertSaveSuccess:"Template saved!", alertSaveError:"Template name and content cannot be empty!", alertDeleteConfirm:"Are you sure you want to delete the template", alertDeleteError:"Please select a template to delete first!", alertCopyError:"Failed to copy. See console for details.", alertSubmitError:"Could not find the input box for the current site.", alertTemplateError:"Please select or create a template first!", alertCannotDeleteDefault:"The default template cannot be deleted.", alertNoUserInput:"Input box is empty. Type your prompt first, then apply the template.", settingsTitle:"Basic Settings", settingTop:"Container top offset (px)", settingToggleWidth:"Helper button width (px)", settingToggleHeight:"Helper button height (px)", settingsSave:"Save Settings", settingsReset:"Reset Defaults", importBtn:"Import", exportBtn:"Export", alertExportEmpty:"No templates to export (default is excluded).", alertExportDone:"Templates exported as file: ", alertImportInvalid:"Import failed: invalid or empty file.", alertImportDone:(added,renamed)=>`Imported ${added} templates (${renamed} renamed to avoid conflicts).`, quickApplyBtn:"Quick Apply", siteDefaultsTitle:"Set Site Default Templates", siteDefaultsList:"Saved Rules", sitePattern:"Domain / Wildcard", siteTemplate:"Bound Template", siteNewBtn:"New Rule", siteSaveBtn:"Save Rule", siteDeleteBtn:"Delete Rule", themeToggleLightTitle:"Toggle dark mode", themeToggleDarkTitle:"Toggle light mode" } }; let currentLang=GM_getValue(LANG_STORE_KEY,'zh'); let uiSettings = loadUISettings(); let siteDefaults = loadSiteDefaults(); let currentTheme = loadTheme(); // 'light' | 'dark' function injectStyles(){ GM_addStyle(` #prompt-helper-container { all: initial !important; } #prompt-helper-container *, #prompt-helper-container *::before, #prompt-helper-container *::after { all: unset !important; box-sizing: border-box !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important; margin: 0 !important; padding: 0 !important; text-decoration: none !important; border: none !important; outline: none !important; } /* 主题变量(默认:明亮) */ #prompt-helper-container { --ph-bg: #f8f9fa; --ph-text: #333333; --ph-border: #dee2e6; --ph-input-bg: #ffffff; --ph-input-border: #adb5bd; --ph-header-text: #343a40; --ph-focus: rgba(0,123,255,.25); position: fixed !important; top: var(--ph-top, 100px) !important; right: 0 !important; z-index: 99999 !important; font-size: 16px !important; color: var(--ph-text) !important; line-height: 1.5 !important; } /* 夜间主题覆盖 */ #prompt-helper-container[data-theme="dark"] { --ph-bg: #1f2329; --ph-text: #e6e6e6; --ph-border: #3b4048; --ph-input-bg: #2b2f36; --ph-input-border: #4a4f57; --ph-header-text: #e6e6e6; --ph-focus: rgba(0,123,255,.35); } /* Helper 主按钮:蓝色增强对比 */ #prompt-helper-toggle { width: var(--ph-toggle-width, 120px) !important; height: var(--ph-toggle-height, 40px) !important; background-color: #007bff !important; color: #fff !important; border-radius: 10px 0 0 10px !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; font-size: 16px !important; box-shadow: -2px 2px 5px rgba(0,0,0,0.2) !important; white-space: nowrap !important; padding: 0 12px !important; } /* 快速应用按钮:绿色 */ #prompt-helper-quickapply { width: var(--ph-toggle-width, 120px) !important; height: 32px !important; margin-top: 6px !important; background-color: #28a745 !important; color: #fff !important; border-radius: 10px 0 0 10px !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; font-size: 13px !important; box-shadow: -2px 2px 5px rgba(0,0,0,0.18) !important; white-space: nowrap !important; padding: 0 10px !important; } #prompt-helper-quickapply:hover { filter: brightness(0.95) !important; } #prompt-helper-content { position: absolute !important; top: 0 !important; right: var(--ph-toggle-width, 120px) !important; width: 400px !important; background-color: var(--ph-bg) !important; border: 1px solid var(--ph-border) !important; border-radius: 8px !important; box-shadow: -2px 2px 10px rgba(0,0,0,0.1) !important; padding: 14px !important; display: flex !important; flex-direction: column !important; gap: 10px !important; transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out !important; color: var(--ph-text) !important; text-align: left !important; max-height: calc(100vh - var(--ph-top, 100px) - 40px) !important; overflow-y: auto !important; overscroll-behavior: contain !important; -webkit-overflow-scrolling: touch !important; padding-bottom: 14px !important; } #prompt-helper-content.hidden { transform: translateX(100%) !important; opacity: 0 !important; pointer-events: none !important; } #prompt-helper-content h3 { padding: 0 !important; font-size: 18px !important; color: var(--ph-header-text) !important; text-align: center !important; font-weight: bold !important; } /* 主/设双页互斥 */ #prompt-helper-content[data-view="main"] #ph-settings-view { display: none !important; } #prompt-helper-content[data-view="settings"] #ph-main-view { display: none !important; } /* 区块与间距(适度紧凑但不重叠) */ #prompt-helper-content .ph-section { display: flex !important; flex-direction: column !important; gap: 6px !important; } #prompt-helper-content .ph-section + .ph-section { margin-top: 8px !important; } /* 表单外观(可见边框 + 聚焦高亮) */ #prompt-helper-content select, #prompt-helper-content input, #prompt-helper-content textarea { appearance: auto !important; -webkit-appearance: auto !important; -moz-appearance: auto !important; width: 100% !important; padding: 8px !important; border: 1px solid var(--ph-input-border) !important; border-radius: 6px !important; background-color: var(--ph-input-bg) !important; color: var(--ph-text) !important; line-height: 1.5 !important; background-clip: padding-box !important; } #prompt-helper-content textarea { resize: vertical !important; min-height: 100px !important; } #prompt-helper-content #ph-template-body { height: 150px !important; } #prompt-helper-content select:focus, #prompt-helper-content input:focus, #prompt-helper-content textarea:focus { border-color: #80bdff !important; box-shadow: 0 0 0 3px var(--ph-focus) !important; outline: none !important; } /* 按钮组 */ #prompt-helper-content .ph-button-group { display: flex !important; gap: 8px !important; justify-content: space-between !important; margin-top: 6px !important; } #prompt-helper-content .ph-button-group button { flex-grow: 1 !important; } #prompt-helper-content button { padding: 10px !important; border-radius: 5px !important; border: none !important; cursor: pointer !important; font-size: 14px !important; font-weight: bold !important; transition: background-color 0.2s, color 0.2s !important; color: white !important; } #prompt-helper-content button:disabled { cursor: not-allowed !important; opacity: 0.7 !important; } #prompt-helper-container .ph-btn-primary { background-color: #007bff !important; } #prompt-helper-container .ph-btn-primary:hover { background-color: #0056b3 !important; } #prompt-helper-container .ph-btn-secondary { background-color: #6c757d !important; } #prompt-helper-container .ph-btn-secondary:hover { background-color: #5a6268 !important; } #prompt-helper-container .ph-btn-success { background-color: #28a745 !important; } #prompt-helper-container .ph-btn-success:hover { background-color: #218838 !important; } #prompt-helper-container .ph-btn-danger { background-color: #dc3545 !important; } #prompt-helper-container .ph-btn-danger:hover { background-color: #c82333 !important; } #prompt-helper-container button:focus-visible, #prompt-helper-container select:focus-visible, #prompt-helper-container input:focus-visible, #prompt-helper-container textarea:focus-visible { outline: 2px solid #0056b3 !important; outline-offset: 2px !important; } /* 头部 */ #prompt-helper-container .ph-header { display: flex !important; justify-content: space-between !important; align-items: center !important; margin-bottom: 6px !important; padding: 0 !important; } #prompt-helper-container .ph-header-right { display: flex !important; align-items: center !important; gap: 8px !important; } #prompt-helper-container #ph-collapse-btn { font-size: 24px !important; cursor: pointer !important; color: #6c757d !important; background: none !important; padding: 0 5px !important; line-height: 1 !important; } /* 语言/设置/主题按钮(随主题变色) */ #prompt-helper-container #ph-lang-toggle, #prompt-helper-container #ph-settings-btn, #prompt-helper-container #ph-theme-btn { font-size: 12px !important; color: var(--ph-text) !important; background: var(--ph-input-bg) !important; border: 1px solid var(--ph-input-border) !important; padding: 2px 8px !important; border-radius: 4px !important; cursor: pointer !important; } #prompt-helper-container #ph-lang-toggle:hover, #prompt-helper-container #ph-settings-btn:hover, #prompt-helper-container #ph-theme-btn:hover { background: rgba(0,0,0,0.06) !important; } #prompt-helper-container[data-theme="dark"] #ph-lang-toggle:hover, #prompt-helper-container[data-theme="dark"] #ph-settings-btn:hover, #prompt-helper-container #ph-theme-btn:hover { background: #353b43 !important; } /* 设置页 */ #prompt-helper-container #ph-settings-view { display: flex !important; flex-direction: column !important; gap: 10px !important; } #prompt-helper-content .ph-grid { display: grid !important; grid-template-columns: 1fr 1fr !important; gap: 8px 10px !important; } @media (max-width: 480px) { #prompt-helper-content .ph-grid { grid-template-columns: 1fr !important; } } `); } function applyUISettings(container){ if(!container) return; container.style.setProperty('--ph-top', `${uiSettings.top}px`); container.style.setProperty('--ph-toggle-width', `${uiSettings.toggleWidth}px`); container.style.setProperty('--ph-toggle-height', `${uiSettings.toggleHeight}px`); } // —— 文本域/富文本工具 ——(保持原逻辑 + Claude 专用 html-direct) function resolveEditableTarget(el){ if(!el) return null; const tag = el.tagName?.toLowerCase?.(); if(tag==='textarea' || tag==='input') return el; if(el.getAttribute?.('contenteditable')==='true' || el.isContentEditable) return el; const inner = el.querySelector?.('[contenteditable="true"], textarea, input, [role="textbox"]'); return inner || el; } function clearEditableContent(el){ el = resolveEditableTarget(el); if(!el) return; try{ el.focus(); try{ document.execCommand('selectAll', false, null); }catch(_){} ['keydown','keypress','keyup'].forEach((t,i)=>{ const ev = tryCreateKeyboardEvent(t,{bubbles:true,cancelable:true,key:'a',code:'KeyA',ctrlKey:true,metaKey:navigator.platform.includes('Mac')}); setTimeout(()=>{ try{ el.dispatchEvent(ev);}catch(_){ } }, i*5); }); try{ el.dispatchEvent(tryCreateInputEvent('beforeinput',{bubbles:true,cancelable:true,inputType:'deleteByCut',data:''})); }catch(_){} try{ el.dispatchEvent(tryCreateInputEvent('input',{bubbles:true,cancelable:true,inputType:'deleteByCut',data:''})); }catch(_){} try{ document.execCommand('delete'); }catch(_){} try{ el.innerHTML=''; }catch(_){ try{ el.textContent=''; }catch(__){} } try{ el.dispatchEvent(new Event('input',{bubbles:true})); }catch(_){} try{ el.dispatchEvent(new Event('change',{bubbles:true})); }catch(_){} }catch(e){ try{ el.innerHTML=''; }catch(_){ try{ el.textContent=''; }catch(__){} } try{ el.dispatchEvent(new Event('input',{bubbles:true})); }catch(_){} try{ el.dispatchEvent(new Event('change',{bubbles:true})); }catch(_){} } } function replaceContentEditable(el, text, opts={}){ el = resolveEditableTarget(el); if(!el) return; const mode = opts.mode || 'default'; const clearBefore = !!opts.clearBefore; const pmStrict = !!opts.pmStrict; try{ el.focus(); if(clearBefore){ clearEditableContent(el); } const sel = window.getSelection(); const range = document.createRange(); range.selectNodeContents(el); sel.removeAllRanges(); sel.addRange(range); if(mode === 'html-direct'){ const html = pmStrict ? textToProseMirrorParagraphHTML(text) : textToHtmlPreserveBlankLines(text); let ok = false; try{ ok = document.execCommand('insertHTML', false, html); }catch(_){ ok = false; } if(!ok){ try{ el.innerHTML = html; }catch(__){ el.textContent = text; } } } else if(mode === 'paste-only'){ let ok=false; try{ const html = pmStrict ? textToProseMirrorParagraphHTML(text) : textToHtmlPreserveBlankLines(text); const dt=new DataTransfer(); dt.setData('text/plain', text); dt.setData('text/html', html); const evt=new ClipboardEvent('paste',{bubbles:true,cancelable:true,clipboardData:dt}); ok=el.dispatchEvent(evt); if(!ok){ el.innerHTML = html; } }catch(_){ try{ el.innerHTML = (pmStrict ? textToProseMirrorParagraphHTML(text) : textToHtmlPreserveBlankLines(text)); } catch(__){ el.textContent = text; } } } else { try { document.execCommand('insertText', false, text); } catch(_) {} pasteIntoProseMirror(el, text, { pmStrict }); } el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); ['compositionstart','compositionupdate','compositionend'].forEach(t=>el.dispatchEvent(new Event(t,{bubbles:true}))); }catch(e){ try{ el.textContent = text; }catch(_){} el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); } } function buildUI(){ const create=(tag,id,classes=[],attributes={},children=[])=>{ const el=document.createElement(tag); if(id)el.id=id; if(classes.length)el.classList.add(...classes); for(const k in attributes)el.setAttribute(k,attributes[k]); for(const c of children)el.appendChild(c); return el; }; const D={}; const container=create('div','prompt-helper-container'); D.toggleButton=create('button','prompt-helper-toggle'); D.quickApplyButton=document.createElement('button'); D.quickApplyButton.id='prompt-helper-quickapply'; D.contentPanel=create('div','prompt-helper-content',['hidden']); D.contentPanel.setAttribute('data-view','main'); // 头部 D.langToggleButton=create('button','ph-lang-toggle',[],{},[document.createTextNode('中/En')]); D.title=document.createElement('h3'); D.title.id='ph-title'; D.themeButton=document.createElement('button'); D.themeButton.id='ph-theme-btn'; D.settingsButton=document.createElement('button'); D.settingsButton.id='ph-settings-btn'; D.settingsButton.appendChild(document.createTextNode('⚙️')); D.collapseButton=create('button','ph-collapse-btn',[],{id:'ph-collapse-btn'},[document.createTextNode('\u00d7')]); // 右侧按钮排列:主题切换 → 设置 → 关闭 const rightBox=create('div',null,['ph-header-right'],{},[D.themeButton, D.settingsButton, D.collapseButton]); const header=create('div','ph-header',['ph-header'],{},[D.langToggleButton,D.title,rightBox]); // 主视图 D.labelSelect=create('label','ph-label-select',[],{for:'ph-template-select'}); D.templateSelect=create('select','ph-template-select'); D.newBtn=create('button','ph-new-btn',['ph-btn-primary']); D.saveBtn=create('button','ph-save-btn',['ph-btn-success']); D.deleteBtn=create('button','ph-delete-btn',['ph-btn-danger']); const section1=create('div',null,['ph-section'],{},[ D.labelSelect,D.templateSelect, create('div',null,['ph-button-group'],{},[D.newBtn,D.saveBtn,D.deleteBtn]) ]); D.labelName=create('label','ph-label-name',[],{for:'ph-template-name'}); D.templateNameInput=create('input','ph-template-name',[],{type:'text'}); D.labelContent=create('label','ph-label-content',[],{for:'ph-template-body'}); D.templateBodyTextarea=create('textarea','ph-template-body'); const section2=create('div',null,['ph-section'],{},[ D.labelName,D.templateNameInput,D.labelContent,D.templateBodyTextarea ]); D.copyBtn=create('button','ph-copy-btn',['ph-btn-secondary']); D.submitBtn=create('button','ph-submit-btn',['ph-btn-primary']); const sectionActions=create('div',null,['ph-section'],{},[ create('div',null,['ph-button-group'],{},[D.copyBtn,D.submitBtn]) ]); D.mainView = create('div','ph-main-view',[],{},[section1,section2,sectionActions]); // 设置视图 —— 基础 D.importBtn=create('button','ph-import-btn',['ph-btn-secondary']); D.exportBtn=create('button','ph-export-btn',['ph-btn-secondary']); D.importFileInput=create('input','ph-import-file',[],{type:'file',accept:'.json,application/json',style:'display:none'}); const sectionIO=create('div',null,['ph-section'],{},[ create('div',null,['ph-button-group'],{},[D.importBtn,D.exportBtn]), D.importFileInput ]); D.settingsTitleEl = document.createElement('h4'); D.settingsTitleEl.id='ph-settings-title'; D.settingTopLabel = document.createElement('label'); D.settingTopLabel.htmlFor='ph-setting-top'; D.settingTopInput = document.createElement('input'); D.settingTopInput.id='ph-setting-top'; D.settingTopInput.type='number'; D.settingTopInput.min='0'; D.settingTopInput.step='1'; D.settingToggleWidthLabel = document.createElement('label'); D.settingToggleWidthLabel.htmlFor='ph-setting-toggle-width'; D.settingToggleWidthInput = document.createElement('input'); D.settingToggleWidthInput.id='ph-setting-toggle-width'; D.settingToggleWidthInput.type='number'; D.settingToggleWidthInput.min='40'; D.settingToggleWidthInput.step='1'; D.settingToggleHeightLabel = document.createElement('label'); D.settingToggleHeightLabel.htmlFor='ph-setting-toggle-height'; D.settingToggleHeightInput = document.createElement('input'); D.settingToggleHeightInput.id='ph-setting-toggle-height'; D.settingToggleHeightInput.type='number'; D.settingToggleHeightInput.min='24'; D.settingToggleHeightInput.step='1'; D.settingsSaveBtn = create('button','ph-settings-save',['ph-btn-success']); D.settingsResetBtn = create('button','ph-settings-reset',['ph-btn-secondary']); const settingsGrid = create('div','ph-settings-grid',['ph-grid'],{},[ D.settingTopLabel, D.settingTopInput, D.settingToggleWidthLabel, D.settingToggleWidthInput, D.settingToggleHeightLabel, D.settingToggleHeightInput ]); const settingsButtons = create('div',null,['ph-button-group'],{},[D.settingsSaveBtn, D.settingsResetBtn]); // 设置视图 —— 站点默认模板 D.siteTitle = document.createElement('h4'); D.siteTitle.id='ph-site-title'; D.siteListLabel = document.createElement('label'); D.siteListLabel.htmlFor='ph-site-list'; D.siteList = document.createElement('select'); D.siteList.id='ph-site-list'; D.siteNewBtn = create('button','ph-site-new',['ph-btn-primary']); D.siteSaveBtn = create('button','ph-site-save',['ph-btn-success']); D.siteDeleteBtn = create('button','ph-site-del',['ph-btn-danger']); const siteListRow = create('div',null,['ph-section'],{},[ D.siteListLabel, D.siteList, create('div',null,['ph-button-group'],{},[D.siteNewBtn, D.siteSaveBtn, D.siteDeleteBtn]) ]); D.sitePatternLabel = document.createElement('label'); D.sitePatternLabel.htmlFor='ph-site-pattern'; D.sitePatternInput = document.createElement('input'); D.sitePatternInput.id='ph-site-pattern'; D.sitePatternInput.type='text'; D.sitePatternInput.placeholder='e.g. *.example.com'; D.siteTplLabel = document.createElement('label'); D.siteTplLabel.htmlFor='ph-site-tpl'; D.siteTplSelect = document.createElement('select'); D.siteTplSelect.id='ph-site-tpl'; const siteEditGrid = create('div','ph-site-grid',['ph-grid'],{},[ D.sitePatternLabel, D.sitePatternInput, D.siteTplLabel, D.siteTplSelect ]); D.backBtn = create('button','ph-back-btn',['ph-btn-secondary'],{},[document.createTextNode('←')]); D.settingsView = document.createElement('div'); D.settingsView.id='ph-settings-view'; D.settingsView.append( D.backBtn, D.siteTitle, siteListRow, siteEditGrid, D.settingsTitleEl, settingsGrid, settingsButtons, sectionIO ); D.contentPanel.append(header, D.mainView, D.settingsView); const containerNodes = [D.toggleButton, D.quickApplyButton, D.contentPanel]; container.append(...containerNodes); return {container,elements:D}; } function nowStamp(){ const d=new Date(); const p=n=>String(n).padStart(2,'0'); return `${d.getFullYear()}${p(d.getMonth()+1)}${p(d.getDate())}_${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}`; } function downloadJSON(filename, obj){ const blob=new Blob([JSON.stringify(obj,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(); setTimeout(()=>{URL.revokeObjectURL(url); a.remove();},0); } function ensureUniqueNameLegacy(baseName, existingSet){ return ensureUniqueName(baseName, existingSet); } function genId(prefix='id'){ return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2,8)}`; } function normalizeImportedList(parsed){ if(!parsed) return []; if(Array.isArray(parsed)) return parsed; if(Array.isArray(parsed.templates)) return parsed.templates; if(typeof parsed==='object'){ const vals=Object.values(parsed).filter(v=>v && typeof v==='object' && 'name' in v && 'template' in v); if(vals.length) return vals; } return []; } function getCurrentSiteConfig(){ const hostname=window.location.hostname; for(const key in siteConfigs){ if(hostname.includes(key)) return siteConfigs[key]; } return null; } function resolveEditableTargetWrapper(el){ return resolveEditableTarget(el); } function findInputElement(){ const siteConfig=getCurrentSiteConfig(); if(!siteConfig){return null;} let inputElement=null; if(siteConfig.shadowRootSelector){ const host=document.querySelector(siteConfig.shadowRootSelector); if(host&&host.shadowRoot){ const elementInShadow=host.shadowRoot.querySelector(siteConfig.inputSelector); if(elementInShadow) inputElement=elementInShadow; } } if(!inputElement){ const selectors=siteConfig.inputSelector.split(',').map(s=>s.trim()); for(const selector of selectors){ const elements=document.querySelectorAll(selector); for(const element of elements){ if(!element.closest('#prompt-helper-container')){ inputElement=element; break; } } if(inputElement) break; } } inputElement = resolveEditableTargetWrapper(inputElement); return inputElement; } function getTextFromEditable(el){ el = resolveEditableTarget(el); if(!el) return ''; const tag=el.tagName?.toLowerCase?.() || ''; if(tag==='textarea' || tag==='input') return el.value || ''; if(el.getAttribute?.('contenteditable')==='true' || el.isContentEditable){ let t = (el.innerText!==undefined)? el.innerText : (el.textContent||''); return t.replace(/\u200B/g,'').replace(/\s+$/,''); } return (el.value || el.textContent || '').trim(); } function isKimiSite(){ const h=location.hostname; return h.includes('kimi.moonshot.cn')||h.includes('kimi.com')||h.includes('www.kimi.com'); } function isClaudeSite(){ const h=location.hostname; return h.includes('claude.ai')||h.includes('fuclaude.com'); } function patternToRegex(pat){ const esc = String(pat||'').toLowerCase().replace(/[.+^${}()|[\]\\]/g,'\\$&').replace(/\*/g,'.*'); return new RegExp('^'+esc+'$','i'); } function matchHostWithPattern(host, pat){ if(!host || !pat) return false; const re = patternToRegex(pat); return re.test(String(host).toLowerCase()); } function applyPromptToChat(inputElement, finalPrompt){ inputElement = resolveEditableTarget(inputElement); if(inputElement.tagName?.toLowerCase?.()==='textarea'){ if(location.hostname.includes('tongyi.com')){ const reactKey=Object.keys(inputElement).find(key=>key.startsWith('__reactInternalInstance')||key.startsWith('__reactFiber')||key.startsWith('__reactProps')); if(reactKey){ try{ const fiberNode=inputElement[reactKey]; const possiblePaths=[fiberNode?.memoizedProps?.onChange,fiberNode?.return?.memoizedProps?.onChange,fiberNode?.return?.return?.memoizedProps?.onChange,fiberNode?.pendingProps?.onChange]; for(const onChange of possiblePaths){ if(onChange&&typeof onChange==='function'){ const fakeEvent={target:{value:finalPrompt},currentTarget:{value:finalPrompt},preventDefault:()=>{},stopPropagation:()=>{}}; onChange(fakeEvent); break; } } }catch(e){ console.log('[PromptHelper] React状态操作失败:',e);} } inputElement.focus(); inputElement.value=''; inputElement.value=finalPrompt; try{ Object.defineProperty(inputElement,'value',{value:finalPrompt,writable:true,configurable:true}); }catch(_){} [ new Event('focus',{bubbles:true}), tryCreateInputEvent('beforeinput',{bubbles:true,cancelable:true,inputType:'insertText',data:finalPrompt}), tryCreateInputEvent('input',{bubbles:true,cancelable:true,inputType:'insertText',data:finalPrompt}), new Event('change',{bubbles:true}), tryCreateKeyboardEvent('keydown',{bubbles:true,key:'a'}), tryCreateKeyboardEvent('keyup',{bubbles:true,key:'a'}), new Event('blur',{bubbles:true}) ].forEach((ev,i)=>setTimeout(()=>inputElement.dispatchEvent(ev),i*10)); setTimeout(()=>{ if(inputElement.value!==finalPrompt) inputElement.value=finalPrompt; inputElement.blur(); setTimeout(()=>{ inputElement.focus(); inputElement.value=finalPrompt; inputElement.dispatchEvent(tryCreateInputEvent('input',{bubbles:true,cancelable:true,data:finalPrompt,inputType:'insertText'})); inputElement.dispatchEvent(new Event('change',{bubbles:true})); inputElement.dispatchEvent(new Event('propertychange',{bubbles:true})); window.dispatchEvent(new Event('resize')); },50); },150); } else if(location.hostname.includes('grok.com')){ inputElement.focus(); setNativeValue(inputElement,''); inputElement.dispatchEvent(new Event('input',{bubbles:true})); setNativeValue(inputElement,finalPrompt); try{ inputElement.setAttribute('value',finalPrompt);}catch(_){} inputElement.dispatchEvent(tryCreateInputEvent('beforeinput',{bubbles:true,cancelable:true,inputType:'insertFromPaste',data:finalPrompt})); inputElement.dispatchEvent(new Event('input',{bubbles:true})); inputElement.dispatchEvent(tryCreateInputEvent('input',{bubbles:true,cancelable:true,inputType:'insertText',data:finalPrompt})); inputElement.dispatchEvent(new Event('change',{bubbles:true})); ['keydown','keypress','keyup'].forEach(type=>inputElement.dispatchEvent(tryCreateKeyboardEvent(type,{bubbles:true,cancelable:true,key:'a',code:'KeyA'}))); try{ inputElement.setSelectionRange(finalPrompt.length,finalPrompt.length);}catch(_){} setTimeout(()=>{ inputElement.dispatchEvent(new Event('input',{bubbles:true})); inputElement.dispatchEvent(new Event('change',{bubbles:true})); },50); } else { if(location.hostname.includes('openai.com')||location.hostname.includes('chatgpt.com')){ inputElement.value=finalPrompt; const isChrome=navigator.userAgent.includes('Chrome')&&!navigator.userAgent.includes('Firefox'); if(isChrome){ setTimeout(()=>{ inputElement.focus(); inputElement.value=finalPrompt; if(typeof inputElement.setSelectionRange==='function') inputElement.setSelectionRange(finalPrompt.length,finalPrompt.length); inputElement.dispatchEvent(tryCreateInputEvent('input',{bubbles:true,cancelable:false,inputType:'insertText'})); let protectionCount=0; const protect=()=>{ if(protectionCount<20){ const cur=inputElement.value; if(cur.replace(/\n/g,'')===finalPrompt.replace(/\n/g,'')&&!cur.includes('\n')&&finalPrompt.includes('\n')){ inputElement.value=finalPrompt; if(typeof inputElement.setSelectionRange==='function') inputElement.setSelectionRange(finalPrompt.length,finalPrompt.length); inputElement.dispatchEvent(tryCreateInputEvent('input',{bubbles:true,cancelable:false,inputType:'insertText'})); } protectionCount++; setTimeout(protect,100);} }; setTimeout(protect,100); },50); } else { inputElement.dispatchEvent(new Event('input',{bubbles:true})); if(typeof inputElement.setSelectionRange==='function') inputElement.setSelectionRange(finalPrompt.length,finalPrompt.length); } } else { inputElement.value=finalPrompt; } if(location.hostname.includes('deepseek.com')){ const parentDiv=inputElement.parentElement; if(parentDiv){ let displayDiv=parentDiv.querySelector('.b13855df'); if(!displayDiv){ const allDivs=parentDiv.querySelectorAll('div'); for(const div of allDivs){ if(!div.classList.contains('_24fad49')&&div!==parentDiv){ displayDiv=div; break; } } } if(displayDiv){ displayDiv.innerHTML=''; finalPrompt.split('\n').forEach((line,idx)=>{ if(idx>0) displayDiv.appendChild(document.createElement('br')); displayDiv.appendChild(document.createTextNode(line)); }); } } } } } else if(inputElement.getAttribute && (inputElement.getAttribute('contenteditable')==='true' || inputElement.isContentEditable)){ if(isKimiSite()){ replaceContentEditable(inputElement, finalPrompt, { mode: 'paste-only', clearBefore: true }); } else if(isClaudeSite()){ replaceContentEditable(inputElement, finalPrompt, { mode: 'html-direct', clearBefore: true, pmStrict: true }); } else if(location.hostname.includes('claude.ai')||location.hostname.includes('fuclaude.com')){ pasteIntoProseMirror(inputElement,finalPrompt,{pmStrict:true}); inputElement.dispatchEvent(new Event('input',{bubbles:true})); inputElement.dispatchEvent(new Event('change',{bubbles:true})); } else { if(location.hostname.includes('openai.com')||location.hostname.includes('chatgpt.com')){ inputElement.innerHTML=''; const lines=finalPrompt.split('\n'); lines.forEach((line,index)=>{ if(index>0) inputElement.appendChild(document.createElement('br')); if(line.length>0) inputElement.appendChild(document.createTextNode(line)); else if(index