// ==UserScript== // @name 提示词模板插入 // @namespace https://github.com/local/deepseek-template-insert // @version 1.0.3 // @description 在 DeepSeek 会话输入框内注入提示词模板面板:搜索、预览、点击即可在光标处插入模板(修复键盘冲突) // @author local // @match https://chat.deepseek.com/* // @match https://www.doubao.com/* // @grant GM_setValue // @grant GM_getValue // @run-at document-idle // @noframes // @license Apache-2.0 // ==/UserScript== (function () { "use strict"; // ====== 输入框匹配与按钮尺寸等常量配置 ====== var INPUT_SELECTOR = "[contenteditable], textarea"; var STORAGE_KEY = "ds_prompt_templates_v1"; var BTN_SIZE = 28; var PANEL_WIDTH = 460; // 桌面端面板宽度(移动端自动缩放) var PANEL_HEIGHT = 480; // 固定面板高度,内容变化不改变UI尺寸 var PRESET_TEMPLATES = [ { name: "简洁回答", content: "请直接回答用户问题,不要有过多解释,用户问题如下:", }, ]; // ====== 全局状态 ====== var templates = loadTemplates(); var lastInput = null; var root = null; var panelOpen = false; var lastFilter = ""; var lastMatched = []; var activeIndex = -1; var savedListScroll = 0; var sizeObs = null; var restoreFocusEl = null; // 打开面板前聚焦的元素,关闭时恢复焦点 var inserting = false; // 标记是否正在插入模板,避免关闭面板时错误恢复焦点 // ====== 工具函数 ====== function genId() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 8); } function loadTemplates() { var raw = null; try { raw = JSON.parse(GM_getValue(STORAGE_KEY, "null")); } catch (e) { raw = null; } if (!Array.isArray(raw)) { raw = PRESET_TEMPLATES.map(function (t) { return { id: genId(), name: t.name, content: t.content }; }); GM_setValue(STORAGE_KEY, JSON.stringify(raw)); return raw; } var before = raw.length; raw = raw.filter(function (t) { return t && typeof t === "object" && typeof t.name === "string" && typeof t.content === "string"; }); raw.forEach(function (t) { if (typeof t.id !== "string" || !t.id) t.id = genId(); }); if (raw.length !== before) GM_setValue(STORAGE_KEY, JSON.stringify(raw)); return raw; } function saveTemplates() { GM_setValue(STORAGE_KEY, JSON.stringify(templates)); } // 查找当前可见的输入框:优先当前焦点元素,否则取最靠下的可见输入框 function findInput() { var active = document.activeElement; if (active && active.matches && active.matches(INPUT_SELECTOR)) { var ar = active.getBoundingClientRect(); if (ar.width > 0 && ar.height > 0) return active; } var els = Array.prototype.slice.call(document.querySelectorAll(INPUT_SELECTOR)); var visible = els.filter(function (el) { var rect = el.getBoundingClientRect(); return rect.width > 0 && rect.height > 0; }); if (!visible.length) return null; visible.sort(function (a, b) { return b.getBoundingClientRect().bottom - a.getBoundingClientRect().bottom; }); return visible[0]; } // 检测暗色主题:依次检查 html、body 的 class / data 属性,最后回退到系统偏好 function detectDark() { function check(el) { if (!el) return null; if (el.classList.contains("dark")) return true; if (el.classList.contains("light")) return false; var mode = el.getAttribute("data-mode") || el.getAttribute("data-theme"); if (mode) return mode === "dark"; return null; } var res = check(document.documentElement) || check(document.body); if (res !== null) return res; return window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; } // 注入样式(含移动端媒体查询和固定高度) function injectStyle() { var id = "dst-style"; if (document.getElementById(id)) return; var style = document.createElement("style"); style.id = id; style.textContent = '.dst-root{position:fixed;inset:0;pointer-events:none;z-index:2147483000;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif}' + ".dst-root *{box-sizing:border-box}" + '.dst-root[data-theme="dark"]{--bg:#1f1f1f;--surface:#2a2a2a;--hover:rgba(255,255,255,.06);--border:rgba(255,255,255,.10);--text:#ececec;--text-dim:#9a9a9a;--accent:#4d6bfe;--danger:#f87171}' + '.dst-root[data-theme="light"]{--bg:#ffffff;--surface:#f6f6f6;--hover:rgba(0,0,0,.05);--border:rgba(0,0,0,.10);--text:#1f1f1f;--text-dim:#6b6b6b;--accent:#4d6bfe;--danger:#dc2626}' + ".dst-btn{position:fixed;width:28px;height:28px;border-radius:8px;background:transparent;border:1px solid var(--border);color:var(--text);font-size:16px;line-height:1;display:flex;align-items:center;justify-content:center;cursor:pointer;pointer-events:auto;opacity:1;transition:background .15s ease;padding:0}" + ".dst-btn:hover,.dst-btn.active{background:var(--hover)}" + ".dst-panel{position:fixed;width:min(460px, calc(100vw - 16px));height:" + PANEL_HEIGHT + "px;max-height:70vh;min-height:0;display:flex;flex-direction:column;background:var(--bg);border:1px solid var(--border);border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.35);overflow:hidden;pointer-events:auto;animation:dstFadeIn .12s ease}" + "@keyframes dstFadeIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}" + ".dst-search{margin:10px 10px 6px;padding:7px 10px;border-radius:6px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:13px;outline:none}" + ".dst-search::placeholder{color:var(--text-dim)}" + ".dst-search:focus{border-color:var(--accent)}" + ".dst-body{display:flex;min-height:0;overflow:hidden;flex:1}" + ".dst-list{width:180px;overflow-y:auto;border-right:1px solid var(--border);flex-shrink:0;padding:4px 0}" + ".dst-item{padding:8px 10px;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:4px;color:var(--text);font-size:13px}" + ".dst-item:hover{background:var(--hover)}" + ".dst-item.active{background:var(--hover);box-shadow:inset 2px 0 0 var(--accent)}" + ".dst-item-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}" + ".dst-item-ops{display:none;gap:2px;flex-shrink:0}" + ".dst-item:hover .dst-item-ops{display:inline-flex}" + ".dst-op-btn{background:none;border:none;cursor:pointer;font-size:12px;padding:2px 4px;color:var(--text-dim);line-height:1}" + ".dst-op-btn:hover{color:var(--text)}" + ".dst-empty{padding:10px;color:var(--text-dim);font-size:13px}" + ".dst-preview{flex:1;overflow-y:auto;padding:10px;white-space:pre-wrap;word-break:break-word;color:var(--text);font-size:13px;line-height:1.6}" + ".dst-preview.dst-placeholder{color:var(--text-dim)}" + ".dst-create{margin:6px 10px 10px;padding:8px;border-radius:6px;border:none;background:var(--accent);color:#fff;font-size:13px;cursor:pointer}" + ".dst-create:hover{filter:brightness(1.1)}" + ".dst-modal{position:fixed;inset:0;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center;pointer-events:auto;z-index:2147483001}" + ".dst-modal-card{width:420px;max-width:90vw;background:var(--bg);border:1px solid var(--border);border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,.35);padding:16px}" + ".dst-modal-title{font-size:15px;font-weight:600;color:var(--text);margin-bottom:12px}" + ".dst-field{margin-bottom:12px}" + ".dst-name{width:100%;padding:8px 10px;border-radius:6px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:13px;outline:none}" + ".dst-name:focus{border-color:var(--accent)}" + ".dst-content{width:100%;height:180px;padding:8px 10px;border-radius:6px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:13px;outline:none;resize:vertical;font-family:inherit}" + ".dst-content:focus{border-color:var(--accent)}" + ".dst-content::placeholder{color:var(--text-dim)}" + ".dst-error{color:var(--danger);font-size:12px;margin-top:4px;min-height:14px}" + ".dst-modal-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:8px}" + ".dst-cancel{padding:6px 14px;border-radius:6px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:13px;cursor:pointer}" + ".dst-save{padding:6px 14px;border-radius:6px;border:none;background:var(--accent);color:#fff;font-size:13px;cursor:pointer}" + ".dst-save:hover{filter:brightness(1.1)}" + ".dst-danger{padding:6px 14px;border-radius:6px;border:none;background:var(--danger);color:#fff;font-size:13px;cursor:pointer}" + ".dst-danger:hover{filter:brightness(1.1)}" + ".dst-confirm-text{color:var(--text);font-size:13px;margin-bottom:16px}" + ".dst-toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 16px;font-size:13px;pointer-events:none;opacity:0;transition:opacity .2s ease;z-index:2147483002}" + ".dst-toast.show{opacity:1}" + "@media (max-width:600px){.dst-panel{width:calc(100vw - 16px);height:60vh}.dst-list{width:100%;border-right:none}.dst-preview{display:none}}"; document.head.appendChild(style); } // 显示轻提示(用于复制成功等) function showToast(msg, duration) { if (!root) return; var toast = root.querySelector(".dst-toast"); if (!toast) { toast = document.createElement("div"); toast.className = "dst-toast"; root.appendChild(toast); } toast.textContent = msg; toast.classList.add("show"); clearTimeout(toast.__timer); toast.__timer = setTimeout(function () { toast.classList.remove("show"); }, duration || 2000); } // 构建 UI 根容器 function ensureRoot() { if (root && document.body.contains(root)) return root; injectStyle(); root = document.createElement("div"); root.className = "dst-root"; root.setAttribute("data-theme", detectDark() ? "dark" : "light"); // 📋 按钮(放在输入框右上角) var btn = document.createElement("button"); btn.className = "dst-btn"; btn.type = "button"; btn.textContent = "📋"; btn.title = "提示词模板"; btn.addEventListener("mousedown", function (e) { e.preventDefault(); }); btn.addEventListener("click", function () { togglePanel(); }); root.appendChild(btn); // 面板 var panel = document.createElement("div"); panel.className = "dst-panel"; panel.style.display = "none"; panel.innerHTML = '' + '
' + '
' + '
悬停查看预览
' + "
" + ''; root.appendChild(panel); panel.addEventListener("mousedown", function (e) { if (!e.target.closest(".dst-search")) e.preventDefault(); }); // 新建模板按钮 panel.querySelector(".dst-create").addEventListener("click", function () { openEditor(null); }); // 搜索框输入过滤 var search = panel.querySelector(".dst-search"); search.addEventListener("input", function () { lastFilter = search.value; renderList(); }); // 面板内部键盘导航(不再使用全局键盘监听,避免干扰编辑弹窗) panel.addEventListener("keydown", function (e) { if (e.key === "Escape") { e.preventDefault(); closePanel(); return; } if (e.key === "ArrowDown") { e.preventDefault(); moveActive(1); return; } if (e.key === "ArrowUp") { e.preventDefault(); moveActive(-1); return; } if (e.key === "Enter" && activeIndex >= 0 && lastMatched[activeIndex]) { e.preventDefault(); insertTemplate(lastMatched[activeIndex].content); } }); document.body.appendChild(root); return root; } // 定位按钮:输入框右上角 function positionButton() { if (!lastInput || !root) return; var rect = lastInput.getBoundingClientRect(); if (rect.width === 0 && rect.height === 0) return; var btn = root.querySelector(".dst-btn"); btn.style.left = rect.right - BTN_SIZE - 12 + "px"; btn.style.top = rect.top + 12 + "px"; } // 定位面板:按钮上方,右对齐 function positionPanel() { if (!root) return; var btn = root.querySelector(".dst-btn"); var panel = root.querySelector(".dst-panel"); var brect = btn.getBoundingClientRect(); var panelWidth = Math.min(PANEL_WIDTH, window.innerWidth - 16); var left = Math.min(brect.right - panelWidth, window.innerWidth - panelWidth - 8); if (left < 8) left = 8; var bottom = Math.min(window.innerHeight - brect.top + 8, window.innerHeight - 8); panel.style.left = left + "px"; panel.style.bottom = bottom + "px"; } // 渲染模板列表 function renderList() { if (!root) return; var listEl = root.querySelector(".dst-list"); listEl.innerHTML = ""; var q = lastFilter.trim().toLowerCase(); lastMatched = templates.filter(function (t) { if (!q) return true; return t.name.toLowerCase().indexOf(q) !== -1 || t.content.toLowerCase().indexOf(q) !== -1; }); lastMatched.forEach(function (t) { var item = document.createElement("div"); item.className = "dst-item"; var name = document.createElement("span"); name.className = "dst-item-name"; name.textContent = t.name; var ops = document.createElement("span"); ops.className = "dst-item-ops"; var editBtn = document.createElement("button"); editBtn.type = "button"; editBtn.className = "dst-op-btn"; editBtn.textContent = "✎"; editBtn.title = "编辑"; var delBtn = document.createElement("button"); delBtn.type = "button"; delBtn.className = "dst-op-btn"; delBtn.textContent = "✕"; delBtn.title = "删除"; ops.appendChild(editBtn); ops.appendChild(delBtn); item.appendChild(name); item.appendChild(ops); item.addEventListener("mouseenter", function () { activeIndex = lastMatched.indexOf(t); applyActive(); }); item.addEventListener("click", function () { insertTemplate(t.content); }); editBtn.addEventListener("click", function (e) { e.stopPropagation(); openEditor(t.id); }); delBtn.addEventListener("click", function (e) { e.stopPropagation(); openDeleteConfirm(t.id); }); listEl.appendChild(item); }); if (lastMatched.length === 0) { var empty = document.createElement("div"); empty.className = "dst-empty"; empty.textContent = "无匹配模板"; listEl.appendChild(empty); } activeIndex = lastMatched.length ? 0 : -1; applyActive(); } function applyActive() { if (!root) return; var items = root.querySelectorAll(".dst-item"); for (var i = 0; i < items.length; i++) { items[i].classList.toggle("active", i === activeIndex); } if (activeIndex >= 0 && lastMatched[activeIndex]) { showPreview(lastMatched[activeIndex].content); } } function moveActive(delta) { var n = lastMatched.length; if (!n) return; activeIndex = (activeIndex + delta + n) % n; applyActive(); scrollActiveIntoView(); } function scrollActiveIntoView() { if (!root) return; var listEl = root.querySelector(".dst-list"); var items = listEl.querySelectorAll(".dst-item"); if (activeIndex < 0 || activeIndex >= items.length) return; var lr = listEl.getBoundingClientRect(); var ir = items[activeIndex].getBoundingClientRect(); if (ir.top < lr.top) listEl.scrollTop -= lr.top - ir.top; else if (ir.bottom > lr.bottom) listEl.scrollTop += ir.bottom - lr.bottom; } function showPreview(content) { if (!root) return; var preview = root.querySelector(".dst-preview"); if (content) { preview.textContent = content; preview.classList.remove("dst-placeholder"); } else { preview.textContent = "悬停查看预览"; preview.classList.add("dst-placeholder"); } } function openPanel() { if (!root || !lastInput) return; var panel = root.querySelector(".dst-panel"); panelOpen = true; root.querySelector(".dst-btn").classList.add("active"); restoreFocusEl = document.activeElement; renderList(); root.querySelector(".dst-list").scrollTop = savedListScroll; positionPanel(); panel.style.display = "flex"; root.querySelector(".dst-search").value = lastFilter; // 自动聚焦搜索框,键盘导航立即可用 setTimeout(function () { root.querySelector(".dst-search").focus(); }, 0); } function closePanel() { if (!root || !panelOpen) return; panelOpen = false; savedListScroll = root.querySelector(".dst-list").scrollTop; root.querySelector(".dst-panel").style.display = "none"; root.querySelector(".dst-btn").classList.remove("active"); // 恢复焦点(若插入操作已聚焦输入框则跳过) if (!inserting && restoreFocusEl && document.contains(restoreFocusEl)) { restoreFocusEl.focus(); } inserting = false; } function togglePanel() { if (panelOpen) closePanel(); else openPanel(); } // ====== 弹窗与模板编辑 ====== function showModal(html) { var modal = document.createElement("div"); modal.className = "dst-modal"; modal.innerHTML = html; root.appendChild(modal); modal.addEventListener("click", function (e) { if (e.target === modal) modal.remove(); }); modal.addEventListener("keydown", function (e) { if (e.key === "Escape") modal.remove(); }); return modal; } function setError(input, msg) { var field = input.closest(".dst-field"); var err = field.querySelector(".dst-error"); err.textContent = msg; } function clearError(input) { var field = input.closest(".dst-field"); var err = field.querySelector(".dst-error"); if (err) err.textContent = ""; } function openEditor(id) { var t = id ? templates.find(function (x) { return x.id === id; }) : null; var modal = showModal( '
' + '
' + (t ? "编辑模板" : "新建模板") + "
" + '
' + '
' + '
' + "
" ); modal.__templateId = id || null; var nameInput = modal.querySelector(".dst-name"); var contentInput = modal.querySelector(".dst-content"); if (t) { nameInput.value = t.name; contentInput.value = t.content; } nameInput.addEventListener("input", function () { clearError(nameInput); }); contentInput.addEventListener("input", function () { clearError(contentInput); }); contentInput.addEventListener("keydown", function (e) { if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { e.preventDefault(); doSave(modal); } }); modal.querySelector(".dst-cancel").addEventListener("click", function () { modal.remove(); }); modal.querySelector(".dst-save").addEventListener("click", function () { doSave(modal); }); nameInput.focus(); } function doSave(modal) { var nameInput = modal.querySelector(".dst-name"); var contentInput = modal.querySelector(".dst-content"); var name = nameInput.value.trim(); var content = contentInput.value; var ok = true; if (!name) { setError(nameInput, "名称不能为空"); ok = false; } if (!content) { setError(contentInput, "内容不能为空"); ok = false; } if (!ok) return; var id = modal.__templateId; var newId = null; if (id) { var t = templates.find(function (x) { return x.id === id; }); if (t) { t.name = name; t.content = content; } } else { var tpl = { id: genId(), name: name, content: content }; templates.push(tpl); newId = tpl.id; lastFilter = ""; var search = root.querySelector(".dst-search"); if (search) search.value = ""; } saveTemplates(); modal.remove(); renderList(); if (newId) { activeIndex = templates.findIndex(function (x) { return x.id === newId; }); applyActive(); scrollActiveIntoView(); } } function openDeleteConfirm(id) { var t = templates.find(function (x) { return x.id === id; }); if (!t) return; var modal = showModal( '
' + '
删除模板
' + '
' + '
' + "
" ); modal.querySelector(".dst-confirm-text").textContent = "确定删除模板「" + t.name + "」?此操作不可恢复。"; modal.querySelector(".dst-cancel").addEventListener("click", function () { modal.remove(); }); modal.querySelector(".dst-danger").addEventListener("click", function () { templates = templates.filter(function (x) { return x.id !== id; }); saveTemplates(); modal.remove(); renderList(); }); } // ====== 文本插入 ====== function placeCaretAtEnd(input) { var sel = window.getSelection(); var range = document.createRange(); var walker = document.createTreeWalker(input, NodeFilter.SHOW_TEXT); var lastText = null; while (walker.nextNode()) lastText = walker.currentNode; if (lastText) { range.setStart(lastText, lastText.nodeValue.length); range.setEnd(lastText, lastText.nodeValue.length); } else { var children = input.children; var lastChild = children[children.length - 1]; if (lastChild) { range.selectNodeContents(lastChild); range.collapse(false); } else { range.selectNodeContents(input); range.collapse(false); } } sel.removeAllRanges(); sel.addRange(range); } function editorText(el) { if (el.tagName === "TEXTAREA" || el.tagName === "INPUT") return el.value; return el.innerText || el.textContent || ""; } function insertIntoTextarea(el, content) { var start = el.selectionStart != null ? el.selectionStart : el.value.length; var before = el.value; var newValue = before.slice(0, start) + content + before.slice(start); if (newValue === before) return false; var setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value"); if (setter && setter.set) setter.set.call(el, newValue); else el.value = newValue; el.selectionStart = el.selectionEnd = start + content.length; el.dispatchEvent(new Event("input", { bubbles: true })); return true; } function copyToClipboard(text) { if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(function () { showToast("模板已复制到剪贴板,请手动粘贴"); }).catch(function () { var ta = document.createElement("textarea"); ta.value = text; document.body.appendChild(ta); ta.select(); document.execCommand("copy"); document.body.removeChild(ta); showToast("模板已复制到剪贴板,请手动粘贴"); }); } else { var ta = document.createElement("textarea"); ta.value = text; document.body.appendChild(ta); ta.select(); document.execCommand("copy"); document.body.removeChild(ta); showToast("模板已复制到剪贴板,请手动粘贴"); } } function insertTemplate(content) { var input = findInput(); if (!input) return; input.focus(); inserting = true; // 标记插入中,closePanel 不清除焦点 if (input.tagName === "TEXTAREA" || input.tagName === "INPUT") { insertIntoTextarea(input, content); input.focus(); closePanel(); return; } var sel = window.getSelection(); var needPlace = !sel.rangeCount || !input.contains(sel.getRangeAt(0).commonAncestorContainer); var doInsert = function () { if (needPlace) placeCaretAtEnd(input); input.focus(); var before = editorText(input); var ok = false; try { ok = document.execCommand("insertText", false, content); } catch (e) { ok = false; } if (!ok || editorText(input) === before) { var ok2 = false; try { var dt = new DataTransfer(); dt.setData("text/plain", content); var pe = new ClipboardEvent("paste", { clipboardData: dt, bubbles: true, cancelable: true }); input.dispatchEvent(pe); ok2 = editorText(input) !== before; } catch (e2) { ok2 = false; } if (!ok2) { // 不再手动插入文本节点,改为复制提示,保护编辑器结构 copyToClipboard(content); } } input.focus(); var s3 = window.getSelection(); if (!s3.rangeCount || !input.contains(s3.getRangeAt(0).commonAncestorContainer)) { placeCaretAtEnd(input); } closePanel(); }; if (needPlace) setTimeout(doInsert, 30); else doInsert(); } // ====== 刷新与监听 ====== function refresh() { var input = findInput(); if (!input) { if (root) root.style.display = "none"; return; } if (input !== lastInput) { lastInput = input; attachSizeObserver(input); var r = ensureRoot(); r.style.display = ""; positionButton(); } else if (root) { root.style.display = ""; positionButton(); } } function updateTheme() { if (!root) return; root.setAttribute("data-theme", detectDark() ? "dark" : "light"); } function attachSizeObserver(input) { if (sizeObs) { sizeObs.disconnect(); sizeObs = null; } if (window.ResizeObserver) { sizeObs = new ResizeObserver(function () { positionButton(); }); sizeObs.observe(input); } } function watch() { var body = document.body || document.documentElement; var timer = null; var mo = new MutationObserver(function () { if (timer) return; timer = setTimeout(function () { timer = null; refresh(); }, 300); // 用 300ms 防抖降低频率 }); mo.observe(body, { childList: true, subtree: true }); // 监听 html 和 body 主题变化 var themeMo = new MutationObserver(updateTheme); themeMo.observe(document.documentElement, { attributes: true, attributeFilter: ["class", "data-mode", "data-theme", "style"], }); if (document.body) { themeMo.observe(document.body, { attributes: true, attributeFilter: ["class", "data-mode", "data-theme", "style"], }); } // 系统主题切换 if (window.matchMedia) { var mq = window.matchMedia("(prefers-color-scheme: dark)"); var themeHandler = function () { updateTheme(); }; if (mq.addEventListener) mq.addEventListener("change", themeHandler); else if (mq.addListener) mq.addListener(themeHandler); } setInterval(function () { if (!lastInput) return; if (!document.contains(lastInput)) { refresh(); return; } if (!sizeObs) attachSizeObserver(lastInput); positionButton(); }, 3000); window.addEventListener("scroll", function () { if (root) { positionButton(); if (panelOpen) positionPanel(); } }, { passive: true }); window.addEventListener("resize", function () { if (root) { positionButton(); if (panelOpen) positionPanel(); } }); // 全局快捷键:Ctrl+Shift+P 打开/关闭面板(不再处理面板导航,避免干扰其他输入) document.addEventListener("keydown", function (e) { if (e.ctrlKey && e.shiftKey && e.key === "P") { e.preventDefault(); togglePanel(); } }); // 点击面板外部关闭 document.addEventListener("click", function (e) { if (panelOpen && root && !root.contains(e.target)) closePanel(); }); } // 启动 refresh(); watch(); })();