您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
PromptHelper:通用于 ChatGPT, Gemini, Claude, Kimi, DeepSeek, 通义、元宝、Google AI Studio、Grok、豆包 的侧边模板助手;主/设分离;导入/导出;从聊天栏读取并回填;Kimi/Claude 专项处理(覆盖、不重复、换行保真)。新增:站点默认模板(通配符、早保存优先);“应用默认模板”一键套用站点默认/全局默认;修复并发覆盖(读-改-写);Helper 按钮改蓝色以适配黑底站点。—— 本版:新增夜间模式(黑色系 UI),一键切换并持久化记忆;Claude 换行保真策略保持。—— 改进版:导入/导出增强(同名标准化、冲突策略、可选跳过重复内容、可选整包导入导出、schema/version 兼容、容错更清晰);设置页标题改为“设置站点默认模板”;导出不再询问是否包含默认模板且默认不导出默认模板。;更新默认模板。
// ==UserScript== // @name PromptHelper // @namespace http://tampermonkey.net/ // @version 1.7.6 // @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 // ==/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<str.length;i++){ hash = ((hash << 5) + hash) + str.charCodeAt(i); hash |= 0; } return (hash >>> 0).toString(16); } function ensureUniqueName(baseName, existingSet){ if(!existingSet.has(baseName)) return baseName; let candidate = `${baseName}${IMPORT_SUFFIX_BASE}`; if(!existingSet.has(candidate)) return candidate; let i=2; while(existingSet.has(`${baseName}${IMPORT_SUFFIX_BASE} ${i}`)) i++; return `${baseName}${IMPORT_SUFFIX_BASE} ${i}`; } function ensureUniqueNameNormalized(baseName, normalizedSet){ let candidate = baseName; let idx = 1; while(normalizedSet.has(normalizeKey(candidate))){ idx++; candidate = (idx===2) ? `${baseName}${IMPORT_SUFFIX_BASE}` : `${baseName}${IMPORT_SUFFIX_BASE} ${idx-1}`; } return candidate; } // 强制 open shadow const originalAttachShadow = Element.prototype.attachShadow; Element.prototype.attachShadow = function(options) { if (SETTINGS.forceOpenShadow && options && options.mode === 'closed') options.mode = 'open'; return originalAttachShadow.call(this, options); }; function tryCreateInputEvent(type, opts = {}) { try { return new InputEvent(type, opts); } catch (_) { return new Event(type, { bubbles: !!opts.bubbles, cancelable: !!opts.cancelable }); } } function tryCreateKeyboardEvent(type, opts = {}) { try { return new KeyboardEvent(type, opts); } catch (_) { return new Event(type, { bubbles: !!opts.bubbles, cancelable: !!opts.cancelable }); } } function escapeHtml(s){return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');} function textToHtmlPreserveBlankLines(text){ const lines=text.split('\n');const paras=[];let buf=[]; const flush=()=>{if(!buf.length)return;const inner=buf.map(ln=>escapeHtml(ln)).join('<br>');paras.push(`<p>${inner}</p>`);buf=[];}; for(let i=0;i<lines.length;i++){const ln=lines[i];if(ln===''){flush();paras.push('<p><br></p>');}else buf.push(ln);}flush(); if(paras.length===0)paras.push('<p><br></p>'); return paras.join(''); } function textToProseMirrorParagraphHTML(text){ const lines = text.split('\n'); if(lines.length===0) return '<p><br></p>'; let html = ''; for(const ln of lines){ if(ln==='') html += '<p><br></p>'; else html += `<p>${escapeHtml(ln)}</p>`; } 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<lines.length-1) inputElement.appendChild(document.createElement('br')); }); const isChrome=navigator.userAgent.includes('Chrome')&&!navigator.userAgent.includes('Firefox'); if(isChrome){ const needEscape=(location.hostname.includes('openai.com')||location.hostname.includes('chatgpt.com')); const htmlWithBreaks=needEscape?escapeHtml(finalPrompt).replace(/\n/g,'<br>'):finalPrompt.replace(/\n/g,'<br>'); setTimeout(()=>{ inputElement.focus(); inputElement.innerHTML=htmlWithBreaks; const range=document.createRange(); const sel=window.getSelection(); range.selectNodeContents(inputElement); range.collapse(false); sel.removeAllRanges(); sel.addRange(range); inputElement.dispatchEvent(tryCreateInputEvent('input',{bubbles:true,cancelable:false,inputType:'insertFromPaste'})); let protectionCount=0; const protect=()=>{ if(protectionCount<20){ const currentHtml=inputElement.innerHTML; const currentText=inputElement.textContent||inputElement.innerText; if(currentText.replace(/\n/g,'')===finalPrompt.replace(/\n/g,'')&&!currentHtml.includes('<br>')&&finalPrompt.includes('\n')){ inputElement.innerHTML=htmlWithBreaks; try{ const r=document.createRange(); const s=window.getSelection(); r.selectNodeContents(inputElement); r.collapse(false); s.removeAllRanges(); s.addRange(r);}catch(_){} } protectionCount++; setTimeout(protect,100); }}; setTimeout(protect,100); },50); } } else { inputElement.textContent=finalPrompt; } } } else { if('value' in inputElement) inputElement.value=finalPrompt; if(inputElement.textContent!==undefined) inputElement.textContent=finalPrompt; if(inputElement.innerText!==undefined) inputElement.innerText=finalPrompt; } const commonEvents = (isKimiSite() || isClaudeSite()) ? ['input','change','keydown','keyup'] : ['input','change','keydown','keyup','paste']; commonEvents.forEach(type=>{ let ev; if(type==='input') ev=tryCreateInputEvent('input',{bubbles:true,cancelable:true,inputType:'insertText',data:finalPrompt}); else if(type==='keydown'||type==='keyup') ev=tryCreateKeyboardEvent(type,{bubbles:true,cancelable:true,key:'a',code:'KeyA'}); else ev=new Event(type,{bubbles:true,cancelable:true}); try{ inputElement.dispatchEvent(ev); }catch(_){} }); if(location.hostname.includes('deepseek.com')){ ['keydown','keypress','keyup'].forEach(t=>inputElement.dispatchEvent(tryCreateKeyboardEvent(t,{bubbles:true,cancelable:true,key:'a',code:'KeyA',which:65,keyCode:65}))); inputElement.dispatchEvent(new Event('compositionstart',{bubbles:true})); inputElement.dispatchEvent(new Event('compositionupdate',{bubbles:true})); inputElement.dispatchEvent(new Event('compositionend',{bubbles:true})); } inputElement.focus(); if(inputElement.tagName && (inputElement.tagName.toLowerCase()==='textarea'||inputElement.type==='text')){ try{ inputElement.setSelectionRange(finalPrompt.length,finalPrompt.length);}catch(_){} } else if(inputElement.getAttribute && inputElement.getAttribute('contenteditable')==='true'){ const range=document.createRange(); const sel=window.getSelection(); if(sel&&inputElement.childNodes.length>0){ range.selectNodeContents(inputElement); range.collapse(false); sel.removeAllRanges(); sel.addRange(range);} } setTimeout(()=>{ try{ inputElement.dispatchEvent(new Event('input',{bubbles:true})); }catch(_){} try{ inputElement.dispatchEvent(new Event('change',{bubbles:true})); }catch(_){} },100); const specialSites=['deepseek.com','tongyi.com','yuanbao.tencent.com','aistudio.google.com','doubao.com']; const currentSite=specialSites.find(site=>location.hostname.includes(site)); if(currentSite){ setTimeout(()=>{ const reactKey=Object.keys(inputElement).find(key=>key.startsWith('__reactInternalInstance')||key.startsWith('__reactFiber')); if(reactKey){ try{ const fiberNode=inputElement[reactKey]; if(fiberNode&&fiberNode.memoizedProps&&typeof fiberNode.memoizedProps.onChange==='function'){ const fakeEvent={target:{value:finalPrompt},currentTarget:{value:finalPrompt},preventDefault:()=>{},stopPropagation:()=>{}}; fiberNode.memoizedProps.onChange(fakeEvent); } }catch(e){ console.log(`[PromptHelper] ${currentSite} React状态更新失败:`,e); } } setTimeout(()=>{ if('value' in inputElement && inputElement.value!==finalPrompt){ inputElement.value=finalPrompt; inputElement.dispatchEvent(new Event('input',{bubbles:true})); } },300); },500); } } function getFinalFromChatByTemplateStr(templateStr){ if(!templateStr) return {error:'no_template'}; const inputEl = findInputElement(); if(!inputEl) return {error:'no_input'}; const userTyped = getTextFromEditable(inputEl); if(!userTyped) return {error:'no_user_input'}; return { inputEl, final: templateStr.replace('{User Question}', userTyped) }; } function buildAndInit(){ const {container,elements:D}=buildUI(); const addToDOM=()=>{ if(document.body){ document.body.appendChild(container); applyUISettings(container); container.setAttribute('data-theme', currentTheme);} else { setTimeout(addToDOM,100);} }; addToDOM(); function loadPromptsLatest(){ let latest = {}; const saved=GM_getValue(PROMPTS_STORE_KEY,null); if(saved){ try{ latest=JSON.parse(saved)||{}; }catch{ latest={}; } } const legacyIds=['prompt_1','prompt_2','prompt_3']; const legacyNames=new Set(['通用回答模板','代码评审模板','英文润色模板']); legacyIds.forEach(id=>{ if(id in latest) delete latest[id]; }); for(const k of Object.keys(latest)){ if(legacyNames.has(latest[k]?.name)) delete latest[k]; } if(!latest[DEFAULT_TEMPLATE_ID]) latest[DEFAULT_TEMPLATE_ID]=defaultPrompts[DEFAULT_TEMPLATE_ID]; return latest; } function savePromptsLatest(obj){ GM_setValue(PROMPTS_STORE_KEY, JSON.stringify(obj||{})); } let prompts = loadPromptsLatest(); function populateSiteTplSelect(){ D.siteTplSelect.textContent=''; for(const id in prompts){ const opt=document.createElement('option'); opt.value=id; opt.textContent=prompts[id].name || id; D.siteTplSelect.appendChild(opt); } } function populateSiteList(){ const t = translations[currentLang]; D.siteList.textContent=''; const noneOpt=document.createElement('option'); noneOpt.value=''; noneOpt.textContent='-- ' + t.siteDefaultsList + ' --'; D.siteList.appendChild(noneOpt); siteDefaults.forEach(rule=>{ const name = prompts[rule.templateId]?.name || `[${rule.templateId}]`; const opt=document.createElement('option'); opt.value=rule.id; opt.textContent=`${rule.pattern} → ${name}`; D.siteList.appendChild(opt); }); } function clearSiteEditFields(){ D.siteList.value=''; D.sitePatternInput.value=''; if(D.siteTplSelect.options.length>0) D.siteTplSelect.selectedIndex=0; } function loadSiteRuleToFields(rule){ if(!rule) return clearSiteEditFields(); D.sitePatternInput.value = rule.pattern || ''; if(rule.templateId && prompts[rule.templateId]) D.siteTplSelect.value = rule.templateId; } const populateDropdown=()=>{ const currentSelection=D.templateSelect.value; D.templateSelect.textContent=''; const defaultOption=document.createElement('option'); defaultOption.value=''; defaultOption.textContent=translations[currentLang].selectDefault; D.templateSelect.appendChild(defaultOption); if(prompts[DEFAULT_TEMPLATE_ID]){ const opt=document.createElement('option'); opt.value=DEFAULT_TEMPLATE_ID; opt.textContent=prompts[DEFAULT_TEMPLATE_ID].name; D.templateSelect.appendChild(opt); } for(const id in prompts){ if(id===DEFAULT_TEMPLATE_ID) continue; const option=document.createElement('option'); option.value=id; option.textContent=prompts[id].name; D.templateSelect.appendChild(option); } if(prompts[currentSelection]) D.templateSelect.value=currentSelection; }; const displaySelectedPrompt=()=>{ const selectedId=D.templateSelect.value; if(selectedId&&prompts[selectedId]){ D.templateNameInput.value=prompts[selectedId].name; D.templateBodyTextarea.value=prompts[selectedId].template; } else { D.templateNameInput.value=''; D.templateBodyTextarea.value=''; } D.deleteBtn.disabled=(selectedId===DEFAULT_TEMPLATE_ID); }; function getActiveDefaultTemplateId(promptsIn){ const host = location.hostname.toLowerCase(); for(const rule of siteDefaults){ if(rule && matchHostWithPattern(host, rule.pattern)){ if(rule.templateId && promptsIn[rule.templateId]){ return rule.templateId; } } } return DEFAULT_TEMPLATE_ID; } function applyActiveDefaultToMainSelect(){ const activeId = getActiveDefaultTemplateId(prompts); if(prompts[activeId]) D.templateSelect.value = activeId; displaySelectedPrompt(); } function updateThemeButtonUI(){ const t = translations[currentLang]; if(currentTheme==='dark'){ D.themeButton.textContent = '☀️'; D.themeButton.title = t.themeToggleDarkTitle; }else{ D.themeButton.textContent = '🌙'; D.themeButton.title = t.themeToggleLightTitle; } } const updateUI=()=>{ const t=translations[currentLang]; D.toggleButton.textContent=t.toggleButton; D.title.textContent=t.panelTitle; D.collapseButton.title=t.collapseTitle; D.settingsButton.title = t.settingsTitle; D.quickApplyButton.textContent = t.quickApplyBtn; updateThemeButtonUI(); D.labelSelect.textContent=t.selectTemplate; D.newBtn.textContent=t.newBtn; D.saveBtn.textContent=t.saveBtn; D.deleteBtn.textContent=t.deleteBtn; D.labelName.textContent=t.templateName; D.templateNameInput.placeholder=t.templateNamePlaceholder; D.labelContent.textContent=t.templateContent; D.copyBtn.textContent=t.copyBtn; D.submitBtn.textContent=t.submitBtn; D.settingsTitleEl.textContent = t.settingsTitle; D.settingTopLabel.textContent = t.settingTop; D.settingToggleWidthLabel.textContent = t.settingToggleWidth; D.settingToggleHeightLabel.textContent = t.settingToggleHeight; D.settingsSaveBtn.textContent = t.settingsSave; D.settingsResetBtn.textContent = t.settingsReset; D.importBtn.textContent=t.importBtn; D.exportBtn.textContent=t.exportBtn; D.siteTitle.textContent = t.siteDefaultsTitle; D.siteListLabel.textContent = t.siteDefaultsList; D.sitePatternLabel.textContent = t.sitePattern; D.siteTplLabel.textContent = t.siteTemplate; D.siteNewBtn.textContent = t.siteNewBtn; D.siteSaveBtn.textContent = t.siteSaveBtn; D.siteDeleteBtn.textContent = t.siteDeleteBtn; populateDropdown(); populateSiteTplSelect(); populateSiteList(); applyActiveDefaultToMainSelect(); D.settingTopInput.value = uiSettings.top; D.settingToggleWidthInput.value = uiSettings.toggleWidth; D.settingToggleHeightInput.value = uiSettings.toggleHeight; document.getElementById('prompt-helper-container')?.setAttribute('data-theme', currentTheme); }; updateUI(); // 交互 D.toggleButton.addEventListener('click',()=>D.contentPanel.classList.remove('hidden')); D.collapseButton.addEventListener('click',()=>D.contentPanel.classList.add('hidden')); D.langToggleButton.addEventListener('click',()=>{ currentLang=currentLang==='zh'?'en':'zh'; GM_setValue(LANG_STORE_KEY,currentLang); updateUI(); }); D.settingsButton.addEventListener('click', ()=> D.contentPanel.setAttribute('data-view','settings')); D.backBtn.addEventListener('click', ()=> D.contentPanel.setAttribute('data-view','main')); // 主题切换(持久化) D.themeButton.addEventListener('click', ()=>{ currentTheme = (currentTheme==='dark') ? 'light' : 'dark'; saveTheme(currentTheme); document.getElementById('prompt-helper-container')?.setAttribute('data-theme', currentTheme); updateThemeButtonUI(); }); // 模板 CRUD D.templateSelect.addEventListener('change',displaySelectedPrompt); D.newBtn.addEventListener('click',()=>{ D.templateSelect.value=''; D.templateNameInput.value=''; D.templateBodyTextarea.value=''; D.templateNameInput.focus(); D.deleteBtn.disabled=true; }); D.saveBtn.addEventListener('click',()=>{ const name=normalizeName(D.templateNameInput.value); const templateText=normalizeName(D.templateBodyTextarea.value); if(!name||!templateText){ alert(translations[currentLang].alertSaveError); return; } let latest = (function(){ let l={}; const s=GM_getValue(PROMPTS_STORE_KEY,null); if(s){ try{ l=JSON.parse(s)||{}; }catch{ l={}; } } if(!l[DEFAULT_TEMPLATE_ID]) l[DEFAULT_TEMPLATE_ID]=defaultPrompts[DEFAULT_TEMPLATE_ID]; return l; })(); let id = D.templateSelect.value || `prompt_${Date.now()}`; latest[id] = { name, template: templateText }; try{ GM_setValue(PROMPTS_STORE_KEY, JSON.stringify(latest)); }catch(e){ alert('保存失败:可能超出存储配额或序列化失败。\n' + (e&&e.message?e.message:String(e))); return; } const saved=GM_getValue(PROMPTS_STORE_KEY,null); try{ prompts = JSON.parse(saved)||{}; }catch{ prompts = latest; } updateUI(); D.templateSelect.value=id; displaySelectedPrompt(); alert(`${translations[currentLang].alertSaveSuccess} "${name}"`); }); D.deleteBtn.addEventListener('click',()=>{ const id=D.templateSelect.value; if(!id){ alert(translations[currentLang].alertDeleteError); return; } if(id===DEFAULT_TEMPLATE_ID){ alert(translations[currentLang].alertCannotDeleteDefault); return; } if(confirm(`${translations[currentLang].alertDeleteConfirm} "${prompts[id].name}"?`)){ let latest = (function(){ let l={}; const s=GM_getValue(PROMPTS_STORE_KEY,null); if(s){ try{ l=JSON.parse(s)||{}; }catch{ l={}; } } return l; })(); delete latest[id]; if(!latest[DEFAULT_TEMPLATE_ID]) latest[DEFAULT_TEMPLATE_ID]=defaultPrompts[DEFAULT_TEMPLATE_ID]; try{ GM_setValue(PROMPTS_STORE_KEY, JSON.stringify(latest)); }catch(e){ alert('删除失败:' + (e&&e.message?e.message:String(e))); return; } const saved=GM_getValue(PROMPTS_STORE_KEY,null); try{ prompts = JSON.parse(saved)||{}; }catch{ prompts = latest; } updateUI(); D.templateSelect.value=DEFAULT_TEMPLATE_ID; displaySelectedPrompt(); } }); // 复制(从聊天栏读取) D.copyBtn.addEventListener('click',()=>{ const template=D.templateBodyTextarea.value; const res = getFinalFromChatByTemplateStr(template); if(res.error==='no_template'){ alert(translations[currentLang].alertTemplateError); return; } if(res.error==='no_input'){ alert(translations[currentLang].alertSubmitError); return; } if(res.error==='no_user_input'){ alert(translations[currentLang].alertNoUserInput); return; } navigator.clipboard.writeText(res.final).then(()=>{ const originalText=D.copyBtn.textContent; D.copyBtn.textContent=translations[currentLang].copiedBtn; D.copyBtn.disabled=true; setTimeout(()=>{ D.copyBtn.textContent=originalText; D.copyBtn.disabled=false; },2000); }).catch(err=>{ console.error('Copy failed:',err); alert(translations[currentLang].alertCopyError); }); }); // 基础设置保存/重置 D.settingsSaveBtn.addEventListener('click', ()=>{ const top = Math.max(0, parseInt(D.settingTopInput.value||DEFAULT_UI.top,10)); const tw = Math.max(40, parseInt(D.settingToggleWidthInput.value||DEFAULT_UI.toggleWidth,10)); const th = Math.max(24, parseInt(D.settingToggleHeightInput.value||DEFAULT_UI.toggleHeight,10)); uiSettings = { top, toggleWidth: tw, toggleHeight: th }; saveUISettings(uiSettings); applyUISettings(document.getElementById('prompt-helper-container')); }); D.settingsResetBtn.addEventListener('click', ()=>{ uiSettings = { ...DEFAULT_UI }; saveUISettings(uiSettings); D.settingTopInput.value = uiSettings.top; D.settingToggleWidthInput.value = uiSettings.toggleWidth; D.settingToggleHeightInput.value = uiSettings.toggleHeight; applyUISettings(document.getElementById('prompt-helper-container')); }); // —— 导出(保持:不导出默认模板;仍询问是否导出整包) —— // D.exportBtn.addEventListener('click',()=>{ let latest=(function(){ let l={}; const s=GM_getValue(PROMPTS_STORE_KEY,null); if(s){ try{ l=JSON.parse(s)||{}; }catch{ l={}; } } if(!l[DEFAULT_TEMPLATE_ID]) l[DEFAULT_TEMPLATE_ID]=defaultPrompts[DEFAULT_TEMPLATE_ID]; return l; })(); // 不再询问“是否包含默认模板”;默认固定为 false const includeDefault = false; const exportBundle = !!window.confirm('是否导出“整包配置”(站点规则 + UI 设置 + 主题/语言)?\n(取消=仅导出模板)'); const list=[]; for(const id in latest){ if(!includeDefault && id===DEFAULT_TEMPLATE_ID) continue; const name=normalizeName(latest[id]?.name||''); const template=normalizeName(latest[id]?.template||''); if(name && template) list.push({name,template}); } if(!list.length){ alert(translations[currentLang].alertExportEmpty); return; } let payload, filename; if(exportBundle){ const siteRules = loadSiteDefaults(); const siteDefaultsByName = siteRules.map(r => ({ pattern: r.pattern, templateName: latest[r.templateId]?.name || null })); payload = { app:'PromptHelper', schema:EXPORT_BUNDLE_SCHEMA, version:1, exportedAt:new Date().toISOString(), templates:list, siteDefaultsByName, uiSettings: loadUISettings(), theme: loadTheme(), lang: GM_getValue(LANG_STORE_KEY,'zh') }; filename=`prompthelper-bundle-${nowStamp()}.json`; }else{ payload={ app:'PromptHelper', schema:EXPORT_SCHEMA, version:1, exportedAt:new Date().toISOString(), templates:list }; filename=`prompthelper-templates-${nowStamp()}.json`; } downloadJSON(filename, payload); alert(translations[currentLang].alertExportDone + filename); }); // —— 导入(保持 1.9.1 的增强能力) —— // D.importBtn.addEventListener('click',()=> D.importFileInput.click()); D.importFileInput.addEventListener('change',(evt)=>{ const file=evt.target.files && evt.target.files[0]; if(!file) return; const reader=new FileReader(); reader.onerror=()=>alert(translations[currentLang].alertImportInvalid); reader.onload=()=>{ try{ const text=String(reader.result||'').trim(); const parsed=text?JSON.parse(text):null; const isBundle = parsed && parsed.schema===EXPORT_BUNDLE_SCHEMA; const arr=normalizeImportedList(parsed); if(!arr.length){ alert(translations[currentLang].alertImportInvalid); D.importFileInput.value=''; return; } let latest=(function(){ let l={}; const s=GM_getValue(PROMPTS_STORE_KEY,null); if(s){ try{ l=JSON.parse(s)||{}; }catch{ l={}; } } if(!l[DEFAULT_TEMPLATE_ID]) l[DEFAULT_TEMPLATE_ID]=defaultPrompts[DEFAULT_TEMPLATE_ID]; return l; })(); const existingNameSet = new Set(Object.values(latest).map(p => normalizeName(p?.name||''))); const existingNormSet = new Set(Object.values(latest).map(p => normalizeKey(p?.name||''))); const nameToId = {}; for(const id in latest){ const key = normalizeKey(latest[id]?.name||''); if(key) if(!(key in nameToId)) nameToId[key]=id; } const existingContentHashes = new Set(Object.values(latest).map(p => djb2Hash(p?.template||''))); let hasNameConflict=false, hasDupContent=false; for(const item of arr){ const nm = normalizeName(item?.name||''); const key = normalizeKey(nm); const body = normalizeName(item?.template||''); const hh = djb2Hash(body); if(!nm || !body) continue; if(existingNormSet.has(key)) hasNameConflict=true; if(existingContentHashes.has(hh)) hasDupContent=true; } let conflictPolicy='rename'; if(hasNameConflict){ const ans = (window.prompt('检测到同名模板。\n选择导入策略:\nR=重命名(默认) S=跳过 O=覆盖', 'R')||'').trim().toUpperCase(); if(ans==='S') conflictPolicy='skip'; else if(ans==='O') conflictPolicy='overwrite'; else conflictPolicy='rename'; } let skipDupByContent=false; if(hasDupContent){ skipDupByContent = !!window.confirm('检测到与现有模板“内容完全相同”的条目。\n是否跳过这些重复内容?\n(取消=不跳过,保持旧行为)'); } let added=0, renamed=0, overwritten=0, skippedByName=0, skippedByContent=0; for(const item of arr){ if(!item) continue; let name = normalizeName(item.name||''); let body = normalizeName(item.template||''); if(!name || !body) continue; const key = normalizeKey(name); const hh = djb2Hash(body); if(skipDupByContent && existingContentHashes.has(hh)){ skippedByContent++; continue; } if(existingNormSet.has(key)){ if(conflictPolicy==='skip'){ skippedByName++; continue; } if(conflictPolicy==='overwrite'){ const targetId = nameToId[key]; if(targetId && targetId!==DEFAULT_TEMPLATE_ID){ latest[targetId] = { name: latest[targetId].name, template: body }; overwritten++; existingContentHashes.add(hh); continue; } } const newName = ensureUniqueNameNormalized(name, existingNormSet); if(newName!==name) renamed++; name = newName; } const newId = genId('prompt'); latest[newId] = { name, template: body }; added++; existingNameSet.add(name); existingNormSet.add(normalizeKey(name)); existingContentHashes.add(hh); const nk = normalizeKey(name); if(!(nk in nameToId)) nameToId[nk]=newId; } try{ GM_setValue(PROMPTS_STORE_KEY, JSON.stringify(latest)); }catch(e){ alert('导入失败:保存到本地存储时出错。\n可能是空间不足或序列化失败。\n' + (e&&e.message?e.message:String(e))); D.importFileInput.value=''; return; } const saved=GM_getValue(PROMPTS_STORE_KEY,null); try{ prompts = JSON.parse(saved)||{}; }catch{ prompts = latest; } updateUI(); if(isBundle){ if(Array.isArray(parsed.siteDefaultsByName) && parsed.siteDefaultsByName.length){ if(window.confirm('检测到“站点默认模板”规则。是否导入这些规则?(按模板名匹配)')){ const nameToId2 = {}; for(const id in prompts){ const key2 = normalizeKey(prompts[id]?.name||''); if(key2 && !(key2 in nameToId2)) nameToId2[key2]=id; } let rules = loadSiteDefaults(); let changed=false; for(const r of parsed.siteDefaultsByName){ const pat = normalizeName(r?.pattern||''); const tname = normalizeName(r?.templateName||''); if(!pat || !tname) continue; const tid = nameToId2[normalizeKey(tname)]; if(!tid) continue; const exists = rules.some(x => normalizeName(x.pattern)===pat && x.templateId===tid); if(exists) continue; rules.push({ id: genId('map'), pattern: pat, templateId: tid, createdAt: Date.now() }); changed=true; } if(changed){ try{ saveSiteDefaults(rules); siteDefaults = loadSiteDefaults(); populateSiteList(); }catch(e){ alert('导入站点规则失败:' + (e&&e.message?e.message:String(e))); } } } } const wantSettings = (parsed.uiSettings || parsed.theme || parsed.lang) && window.confirm('检测到界面设置(位置/大小)与主题/语言。是否导入这些设置?'); if(wantSettings){ try{ if(parsed.uiSettings && typeof parsed.uiSettings==='object'){ const top = Math.max(0, parseInt(parsed.uiSettings.top||DEFAULT_UI.top,10)); const tw = Math.max(40, parseInt(parsed.uiSettings.toggleWidth||DEFAULT_UI.toggleWidth,10)); const th = Math.max(24, parseInt(parsed.uiSettings.toggleHeight||DEFAULT_UI.toggleHeight,10)); uiSettings = { top, toggleWidth: tw, toggleHeight: th }; saveUISettings(uiSettings); applyUISettings(document.getElementById('prompt-helper-container')); } if(parsed.theme && (parsed.theme==='light'||parsed.theme==='dark')){ currentTheme = parsed.theme; saveTheme(currentTheme); document.getElementById('prompt-helper-container')?.setAttribute('data-theme', currentTheme); } if(parsed.lang && (parsed.lang==='zh'||parsed.lang==='en')){ currentLang = parsed.lang; GM_setValue(LANG_STORE_KEY, currentLang); } updateUI(); }catch(e){ alert('导入设置失败:' + (e&&e.message?e.message:String(e))); } } } const baseMsg = translations[currentLang].alertImportDone(added, renamed); const extra = []; if(overwritten) extra.push(`覆盖 ${overwritten}`); if(skippedByName) extra.push(`跳过(同名) ${skippedByName}`); if(skippedByContent) extra.push(`跳过(内容重复) ${skippedByContent}`); alert(extra.length ? `${baseMsg}\n${extra.join(';')}` : baseMsg); D.importFileInput.value=''; }catch(e){ console.error(e); alert(translations[currentLang].alertImportInvalid); D.importFileInput.value=''; } }; reader.readAsText(file,'utf-8'); }); // 站点默认模板(读-改-写,仅改动目标条目) function refreshSiteDefaultsFromStore(){ siteDefaults = loadSiteDefaults(); } D.siteNewBtn.addEventListener('click', ()=> { D.siteList.value=''; D.sitePatternInput.value=''; if(D.siteTplSelect.options.length>0) D.siteTplSelect.selectedIndex=0; D.sitePatternInput.focus(); }); D.siteSaveBtn.addEventListener('click', ()=>{ const t = translations[currentLang]; const pattern = normalizeName(D.sitePatternInput.value||''); const tplId = D.siteTplSelect.value; if(!pattern){ alert(t.alertSitePatternRequired); return; } if(!tplId){ alert(t.alertSiteTemplateRequired); return; } let latest = loadSiteDefaults(); const selectedId = D.siteList.value; if(selectedId){ const idx = latest.findIndex(r=>r.id===selectedId); if(idx>=0){ latest[idx] = { ...latest[idx], pattern, templateId: tplId }; }else{ latest.push({ id: selectedId, pattern, templateId: tplId, createdAt: Date.now() }); } }else{ latest.push({ id: genId('map'), pattern, templateId: tplId, createdAt: Date.now() }); } saveSiteDefaults(latest); refreshSiteDefaultsFromStore(); populateSiteList(); alert(t.alertSiteSaved); const activeId = (function(promptsIn){ const host = location.hostname.toLowerCase(); for(const rule of siteDefaults){ if(rule && matchHostWithPattern(host, rule.pattern)){ if(rule.templateId && promptsIn[rule.templateId]) return rule.templateId; } } return DEFAULT_TEMPLATE_ID; })(prompts); if(prompts[activeId]) D.templateSelect.value=activeId; displaySelectedPrompt(); }); D.siteDeleteBtn.addEventListener('click', ()=>{ const t = translations[currentLang]; const selectedId = D.siteList.value; if(!selectedId){ alert(t.alertSiteSelectFirst); return; } let latest = loadSiteDefaults(); const idx = latest.findIndex(r=>r.id===selectedId); if(idx>=0){ latest.splice(idx,1); } saveSiteDefaults(latest); siteDefaults = loadSiteDefaults(); populateSiteList(); D.siteList.value=''; D.sitePatternInput.value=''; if(D.siteTplSelect.options.length>0) D.siteTplSelect.selectedIndex=0; alert(t.alertSiteDeleted); const activeId = (function(promptsIn){ const host = location.hostname.toLowerCase(); for(const rule of siteDefaults){ if(rule && matchHostWithPattern(host, rule.pattern)){ if(rule.templateId && promptsIn[rule.templateId]) return rule.templateId; } } return DEFAULT_TEMPLATE_ID; })(prompts); if(prompts[activeId]) D.templateSelect.value=activeId; displaySelectedPrompt(); }); D.siteList.addEventListener('change', ()=>{ const id = D.siteList.value; const rule = siteDefaults.find(r=>r.id===id); if(!rule){ D.sitePatternInput.value=''; if(D.siteTplSelect.options.length>0) D.siteTplSelect.selectedIndex=0; return; } D.sitePatternInput.value = rule.pattern || ''; if(rule.templateId && prompts[rule.templateId]) D.siteTplSelect.value = rule.templateId; }); // 应用(主按钮 & 快速应用) D.submitBtn.addEventListener('click',()=>{ const template=D.templateBodyTextarea.value; const res = getFinalFromChatByTemplateStr(template); if(res.error==='no_template'){ alert(translations[currentLang].alertTemplateError); return; } if(res.error==='no_input'){ alert(translations[currentLang].alertSubmitError); return; } if(res.error==='no_user_input'){ alert(translations[currentLang].alertNoUserInput); return; } applyPromptToChat(res.inputEl, res.final); D.contentPanel.classList.add('hidden'); }); D.quickApplyButton.addEventListener('click', ()=>{ const promptsSaved=GM_getValue(PROMPTS_STORE_KEY,null); let promptsLatest = prompts; if(promptsSaved){ try{ promptsLatest = JSON.parse(promptsSaved)||prompts; }catch{} } const activeId = (function(promptsIn){ const host = location.hostname.toLowerCase(); for(const rule of siteDefaults){ if(rule && matchHostWithPattern(host, rule.pattern)){ if(rule.templateId && promptsIn[rule.templateId]) return rule.templateId; } } return DEFAULT_TEMPLATE_ID; })(promptsLatest); const tpl = (promptsLatest[activeId]?.template) || (defaultPrompts[DEFAULT_TEMPLATE_ID]?.template)||''; const res = getFinalFromChatByTemplateStr(tpl); if(res.error==='no_template'){ alert(translations[currentLang].alertTemplateError); return; } if(res.error==='no_input'){ alert(translations[currentLang].alertSubmitError); return; } if(res.error==='no_user_input'){ alert(translations[currentLang].alertNoUserInput); return; } applyPromptToChat(res.inputEl, res.final); document.querySelector('#prompt-helper-content')?.classList.add('hidden'); }); } function init(){ if(document.getElementById('prompt-helper-container')) return; if(window.promptHelperInitialized) return; window.promptHelperInitialized=true; injectStyles(); const {container} = (function(){ const res = buildAndInit(); return res||{}; })(); } if(getCurrentSiteConfig()){ init(); } }); })();