PromptHelper

PromptHelper:通用于 ChatGPT, Gemini, Claude, Kimi, DeepSeek, 通义、元宝、Google AI Studio、Grok、豆包 的侧边模板助手;主/设分离;导入/导出;从聊天栏读取并回填;Kimi/Claude 专项处理(覆盖、不重复、换行保真)。新增:站点默认模板(通配符、早保存优先);“应用默认模板”一键套用站点默认/全局默认;修复并发覆盖(读-改-写);Helper 按钮改蓝色以适配黑底站点。—— 本版:新增夜间模式(黑色系 UI),一键切换并持久化记忆;Claude 换行保真策略保持。—— 改进版:导入/导出增强(同名标准化、冲突策略、可选跳过重复内容、可选整包导入导出、schema/version 兼容、容错更清晰),且不改变默认行为。

Versione datata 13/08/2025. Vedi la nuova versione l'ultima versione.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         PromptHelper
// @namespace    http://tampermonkey.net/
// @version      1.7.3
// @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}`;
    }
    // 用“标准化集合”判断唯一性的重命名(不改变原 ensureUniqueName 在其他处的既有行为)
    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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
    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:`SYSTEM ROLE — "Audit-Grade Researcher"

You are a meticulous research analyst. You MUST perform genuine web research (“Search (Web Browsing)” or “Deep research” when available; via API use tool/function-calling to invoke web_search or equivalent), filter out uncertain/incorrect/irrelevant claims, and produce an audit-friendly, citation-backed reasoning chain.
Do NOT reveal chain-of-thought or internal notes. Output language: Chinese only.

INTERNAL DEEP THOUGHT (PRIVATE, NEVER PRINT):
- T0 (before Gate 1) and T1 (before Gate 3): silently run a Deep Thought Monologue (first-principles → multi-perspective → recursive self-critique → synergistic synthesis).
- 若仍有不确定或冲突,优先进入澄清而非猜测。

MODEL-SPECIFIC (TOOLS & BEHAVIOR):
- If in Chat: use “Search” for recent/fact-sensitive claims; when complexity is high, escalate to “Deep research” for multi-step, cited synthesis.
- If using the API: invoke web_search (tool/function) for retrieval; when available, enforce the 9-section output with Structured Outputs (JSON Schema).
- If browsing/tools are unavailable, STOP and ask to enable them before proceeding. Do not produce conclusions without web access for source-required tasks.

GATED WORKFLOW (Chinese output; do not proceed to conclusions unless Gate 1 passes):
Gate 1 — Clarify First (pre-research):
  识别问题是否含混/信息缺失/矛盾/错误前提。若存在问题,仅输出澄清块:
    • 问题诊断(≤120字)
    • 需要补充的关键信息(2–5条,多选/示例)
    • 可选默认假设(A/B/C…;声明“未确认不进入研究与结论”)
Gate 2 — Mid-Research Check:
  研究中若发现定义/口径/时段/法域冲突或证据矛盾,暂停并回到澄清模式。
Gate 3 — Pre-Final Check:
  结论前核验:所有用作推理前提的断言均有 [S#];计算逐步复核;若仍有缺口,回澄清。

METHOD(仅在 Gate 1 通过后执行):
A) 检索计划:给出你“实际执行”的检索式(引号、逻辑运算、site:/filetype:/date 限制)与动机。
B) 执行检索:打开并对比权威来源;剔除过时/仅观点/不可核验内容;必要时进一步检索补证。
C) 来源与证据表:ID | Title | URL | Publisher | Pub/Update Date | Key Evidence Used | Reliability(High/Med)。
D) 去伪存真记录:列明删除项与理由(过时、观点化、被反证、无法核验、无关)。
E) 已确认事实:仅留可交叉验证事实;关键结论力求 ≥3 个独立来源;每条附 [S#]。
F) 逻辑论证链:逐步推导,步步有 [S#]。
G) 结论:中文作答,给出最优答案与置信度/不确定性范围。
H) 局限与更新触发条件:说明残余不确定性与可能改变结论的新证据。

NUMERICAL RIGOR:
- 展示算式与单位换算的逐步过程;逐位检查关键数字;避免心算跳步。

STYLE:
- 中文输出、措辞凝练;每个依赖联网的断言配 [S#] 内联引用;对明显可疑前提先发问再继续(如“为何 1+1 ≠ 2”需界定数学系统/语义上下文)。

FINAL OUTPUT FORMAT(九段固定):
1) 问题重述(若处于 Clarification Mode,仅输出“问题诊断/信息缺口/可选默认假设”)
2) 检索计划(含实际检索式与时间范围)
3) 来源与证据表(Sources Table)
4) 去伪存真记录(Exclusion Log)
5) 已确认事实清单(全部带 [S#])
6) 结论(最准确答案 + 置信度/范围)
8) 局限与更新触发条件
9) 参考文献(按 [S#] 列完整引文,含链接与访问日期)

USER QUESTION (paste multi-paragraph content between the markers):
<<<BEGIN_USER_QUESTION>>>
{User Question}
<<<END_USER_QUESTION>>>
`
            }
        };

        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:"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[data-theme="dark"] #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){
            // 兼容旧实现(保留原函数名 ensureUniqueName)在别处可能被使用
            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){ // wrapper for findInputElement
            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;
                }
                // 刷到内存并刷新 UI
                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; })();
                const includeDefault = !!window.confirm('导出时是否“包含默认模板”?\n(建议否,保持与旧版一致)');
                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){
                    // 站点规则按“模板名”导出,避免 id 失配
                    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);
            });

            // —— 导入(增强:同名标准化/冲突策略/可选跳过重复/可选整包) —— //
            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;

                        // 判定是否为 bundle
                        const isBundle = parsed && parsed.schema===EXPORT_BUNDLE_SCHEMA;

                        const arr=normalizeImportedList(parsed);
                        if(!arr.length){ alert(translations[currentLang].alertImportInvalid); D.importFileInput.value=''; return; }

                        // 读取最新 prompts 与准备集合
                        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;
                        const incomingNormNames = new Set();
                        const incomingContentHashes = new Set();
                        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;
                            // 记录导入集中自身情况(不强制处理导入内部重名:沿用旧逻辑逐条处理)
                            incomingNormNames.add(key);
                            incomingContentHashes.add(hh);
                        }

                        // 仅在确有冲突/重复时询问一次策略;用户取消/乱填则回到“重命名 + 不跳过重复”(旧行为)
                        let conflictPolicy='rename'; // rename | skip | overwrite
                        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;
                        }

                        // 刷新内存与 UI
                        const saved=GM_getValue(PROMPTS_STORE_KEY,null);
                        try{ prompts = JSON.parse(saved)||{}; }catch{ prompts = latest; }
                        updateUI();

                        // —— 若是 bundle:可选导入站点规则 & 设置 —— //
                        if(isBundle){
                            // 站点规则 byName
                            if(Array.isArray(parsed.siteDefaultsByName) && parsed.siteDefaultsByName.length){
                                if(window.confirm('检测到“站点默认模板”规则。是否导入这些规则?(按模板名匹配)')){
                                    // 重新构建 name->id(标准化)映射(考虑刚刚导入的新模板)
                                    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; // 未匹配到模板(可能用户没导这条或改名)
                                        // 避免重复:相同 pattern + templateId
                                        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)));
                                        }
                                    }
                                }
                            }
                            // UI 设置/主题/语言
                            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);
                                        updateThemeButtonUI();
                                    }
                                    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(); }
    });
})();