您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
novalai prompt editor, by liao, use it, you will know
// ==UserScript== // @name novalai prompt editor // @namespace npm/vite-plugin-monkey // @version 0.1.0 // @author monkey // @icon https://vitejs.dev/logo.svg // @match https://novelai.net/image // @require https://cdn.jsdelivr.net/npm/[email protected]/umd/react.production.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.production.min.js // @grant GM_addStyle // @license MIT // @description novalai prompt editor, by liao, use it, you will know // ==/UserScript== (o=>{if(typeof GM_addStyle=="function"){GM_addStyle(o);return}const e=document.createElement("style");e.textContent=o,document.head.append(e)})(" #root{max-width:1280px;margin:0 auto;padding:2rem;text-align:center}.logo{height:6em;padding:1.5em;will-change:filter}.logo:hover{filter:drop-shadow(0 0 2em #646cffaa)}.logo.react:hover{filter:drop-shadow(0 0 2em #61dafbaa)}@keyframes logo-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (prefers-reduced-motion: no-preference){a:nth-of-type(2) .logo{animation:logo-spin infinite 20s linear}}.card{padding:2em}.read-the-docs{color:#888}:root{font-family:Inter,Avenir,Helvetica,Arial,sans-serif;font-size:16px;line-height:24px;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;place-items:center;min-width:320px;min-height:100vh}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}} "); (function (require$$0, require$$0$1) { 'use strict'; var jsxRuntime = { exports: {} }; var reactJsxRuntime_production_min = {}; /** * @license React * react-jsx-runtime.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var f = require$$0, k = Symbol.for("react.element"), l = Symbol.for("react.fragment"), m$1 = Object.prototype.hasOwnProperty, n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, p = { key: true, ref: true, __self: true, __source: true }; function q(c, a, g) { var b, d = {}, e = null, h = null; void 0 !== g && (e = "" + g); void 0 !== a.key && (e = "" + a.key); void 0 !== a.ref && (h = a.ref); for (b in a) m$1.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]); if (c && c.defaultProps) for (b in a = c.defaultProps, a) void 0 === d[b] && (d[b] = a[b]); return { $$typeof: k, type: c, key: e, ref: h, props: d, _owner: n.current }; } reactJsxRuntime_production_min.Fragment = l; reactJsxRuntime_production_min.jsx = q; reactJsxRuntime_production_min.jsxs = q; { jsxRuntime.exports = reactJsxRuntime_production_min; } var jsxRuntimeExports = jsxRuntime.exports; var client = {}; var m = require$$0$1; { client.createRoot = m.createRoot; client.hydrateRoot = m.hydrateRoot; } const TabList = ({ categories, activeTab, onTabChange, onUpdateCategory, onDeleteCategory, onReorderCategories }) => { const handleDragStart = (e, tab) => { e.dataTransfer.setData("text/plain", tab); }; const handleDragOver = (e) => { e.preventDefault(); }; const handleDrop = (e, targetTab) => { e.preventDefault(); const sourceTab = e.dataTransfer.getData("text/plain"); if (sourceTab !== targetTab && sourceTab !== "历史记录" && targetTab !== "历史记录") { onReorderCategories(sourceTab, targetTab); } }; return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { display: "flex", flexWrap: "wrap", borderBottom: "1px solid rgba(255, 255, 255, 0.1)", marginBottom: "10px", gap: "5px", maxWidth: "100%", overflowX: "auto", whiteSpace: "nowrap", paddingBottom: "5px" }, children: Object.keys(categories).map((tab) => /* @__PURE__ */ jsxRuntimeExports.jsxs( "div", { draggable: tab !== "历史记录", onDragStart: (e) => handleDragStart(e, tab), onDragOver: handleDragOver, onDrop: (e) => handleDrop(e, tab), style: { display: "flex", alignItems: "center", cursor: tab !== "历史记录" ? "grab" : "default" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( "span", { contentEditable: tab !== "历史记录", onBlur: (e) => onUpdateCategory(tab, e.target.textContent), style: { background: "transparent", border: "none", color: activeTab === tab ? "#ffffff" : "rgba(255, 255, 255, 0.5)", padding: "10px 15px", cursor: tab !== "历史记录" ? "pointer" : "default", borderBottom: activeTab === tab ? "2px solid #ffffff" : "none", marginBottom: "-1px", outline: "none" }, onClick: () => onTabChange(tab), children: tab } ), tab !== "历史记录" && /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: (e) => { e.stopPropagation(); onDeleteCategory(tab); }, style: { background: "transparent", border: "none", color: "rgba(255, 255, 255, 0.5)", cursor: "pointer", padding: "0 5px", fontSize: "14px", marginRight: "5px" }, children: "×" } ) ] }, tab )) }); }; const PromptList = ({ prompts, onUpdatePrompt, onDeletePrompt, categoryName, onReorderPrompts }) => { const handleApplyPrompt = (prompt) => { const textarea = document.querySelector("#__next > div.sc-a2d0901c-0.gkbaSQ > div:nth-child(4) > div.sc-780ff592-0.jEOQDN > div:nth-child(3) > div:nth-child(2) > div > div:nth-child(2) > textarea"); if (textarea) { const currentValue = textarea.value; textarea.value = currentValue + (currentValue ? "\n" : "") + prompt; const inputEvent = new Event("input", { bubbles: true }); textarea.dispatchEvent(inputEvent); const changeEvent = new Event("change", { bubbles: true }); textarea.dispatchEvent(changeEvent); textarea.focus(); textarea.setSelectionRange(textarea.value.length, textarea.value.length); } }; const handleDragStart = (e, index) => { e.dataTransfer.setData("text/plain", index.toString()); }; const handleDragOver = (e) => { e.preventDefault(); }; const handleDrop = (e, targetIndex) => { e.preventDefault(); const sourceIndex = parseInt(e.dataTransfer.getData("text/plain")); if (sourceIndex !== targetIndex) { onReorderPrompts(categoryName, sourceIndex, targetIndex); } }; return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { overflowY: "auto", overflowX: "hidden", flex: 1, width: "100%" }, children: prompts.map((item, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs( "div", { draggable: categoryName !== "历史记录", onDragStart: (e) => handleDragStart(e, index), onDragOver: handleDragOver, onDrop: (e) => handleDrop(e, index), style: { background: "rgba(255, 255, 255, 0.05)", borderRadius: "8px", padding: "15px", marginBottom: "10px", position: "relative", cursor: categoryName !== "历史记录" ? "grab" : "default" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { position: "absolute", top: "8px", right: "8px", display: "flex", gap: "8px" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: () => handleApplyPrompt(item.prompt), style: { background: "transparent", border: "none", color: "rgba(255, 255, 255, 0.5)", cursor: "pointer", padding: "4px 8px", fontSize: "14px" }, title: "应用到输入框", children: "←" } ), categoryName !== "历史记录" && /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: () => onDeletePrompt(index), style: { background: "transparent", border: "none", color: "rgba(255, 255, 255, 0.5)", cursor: "pointer", padding: "4px 8px", fontSize: "14px" }, children: "×" } ) ] }), /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", marginBottom: "8px" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { contentEditable: true, onBlur: (e) => onUpdatePrompt(index, "title", e.target.textContent), style: { fontSize: "16px", fontWeight: "bold", color: "#fff", outline: "none", marginRight: "8px" }, children: item.title } ), item.count && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { style: { fontSize: "14px", color: "rgba(255, 255, 255, 0.5)" }, children: [ "(", item.count, "次)" ] }) ] }), /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { contentEditable: true, onBlur: (e) => onUpdatePrompt(index, "prompt", e.target.textContent), style: { fontSize: "14px", color: "rgba(255, 255, 255, 0.7)", lineHeight: "1.4", outline: "none" }, children: item.prompt } ) ] }, index )) }); }; const ActionButtons = ({ onAddCategory, onAddPrompt }) => { return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: "10px", marginBottom: "20px" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: onAddCategory, style: { background: "rgba(255, 255, 255, 0.1)", border: "none", color: "#fff", padding: "8px 15px", borderRadius: "4px", cursor: "pointer", flex: 1 }, children: "新增分类" } ), /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: onAddPrompt, style: { background: "rgba(255, 255, 255, 0.1)", border: "none", color: "#fff", padding: "8px 15px", borderRadius: "4px", cursor: "pointer", flex: 1 }, children: "新增提示词" } ) ] }); }; const Drawer = ({ isOpen, onClose, targetRight, zIndex, categories, activeTab, onAddCategory, onAddPrompt, onUpdateCategory, onUpdatePrompt, onDeleteCategory, onDeletePrompt, onTabChange, onReorderCategories, onReorderPrompts, onExport, onImport }) => { const drawerRef = require$$0.useRef(null); require$$0.useEffect(() => { var _a; if (isOpen) { (_a = drawerRef.current) == null ? void 0 : _a.focus(); } }, [isOpen]); return /* @__PURE__ */ jsxRuntimeExports.jsxs( "div", { ref: drawerRef, tabIndex: -1, className: "drawer", style: { position: "fixed", left: isOpen ? targetRight + 1 : "-100%", top: 0, minWidth: "300px", width: `${Math.min(300 + Object.keys(categories).length * 100, window.innerWidth * 0.8)}px`, height: "100%", background: "rgb(34, 37, 63)", boxShadow: isOpen ? "0 0 20px rgba(0,0,0,0.3)" : "none", transition: "all 0.6s cubic-bezier(0.4, 0, 0.2, 1)", transform: `translateX(${isOpen ? "0" : "-30px"})`, opacity: isOpen ? 1 : 0, zIndex, padding: "20px", color: "#ffffff", display: "flex", flexDirection: "column", backdropFilter: "blur(5px)", WebkitBackdropFilter: "blur(5px)" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: onClose, style: { position: "absolute", top: "10px", right: "10px", background: "transparent", border: "none", color: "rgba(255, 255, 255, 0.5)", cursor: "pointer", fontSize: "20px", padding: "4px 8px", lineHeight: 1, zIndex: 1 }, children: "×" } ), /* @__PURE__ */ jsxRuntimeExports.jsx( TabList, { categories, activeTab, onTabChange, onUpdateCategory, onDeleteCategory, onReorderCategories } ), /* @__PURE__ */ jsxRuntimeExports.jsx( ActionButtons, { onAddCategory, onAddPrompt } ), /* @__PURE__ */ jsxRuntimeExports.jsx( PromptList, { prompts: categories[activeTab] || [], onUpdatePrompt: (index, field, value) => onUpdatePrompt(activeTab, index, field, value), onDeletePrompt: (index) => onDeletePrompt(activeTab, index), categoryName: activeTab, onReorderPrompts } ) ] } ); }; const SearchPrompts = ({ categories = [], onAddPrompt }) => { const [isOpen, setIsOpen] = require$$0.useState(false); const [searchTerm, setSearchTerm] = require$$0.useState(""); const [searchResults, setSearchResults] = require$$0.useState([]); const modalRef = require$$0.useRef(null); const [isDragging, setIsDragging] = require$$0.useState(false); const [dragOffset, setDragOffset] = require$$0.useState({ x: 0, y: 0 }); const [position, setPosition] = require$$0.useState({ x: 100, y: 100 }); const handleMouseDown = (e) => { setIsDragging(true); const rect = modalRef.current.getBoundingClientRect(); setDragOffset({ x: e.clientX - rect.left, y: e.clientY - rect.top }); }; const handleMouseMove = (e) => { if (!isDragging) return; setPosition({ x: e.clientX - dragOffset.x, y: e.clientY - dragOffset.y }); }; const handleMouseUp = () => { setIsDragging(false); }; require$$0.useEffect(() => { if (isDragging) { window.addEventListener("mousemove", handleMouseMove); window.addEventListener("mouseup", handleMouseUp); } return () => { window.removeEventListener("mousemove", handleMouseMove); window.removeEventListener("mouseup", handleMouseUp); }; }, [isDragging, dragOffset]); require$$0.useEffect(() => { if (!searchTerm.trim() || !Array.isArray(categories)) { setSearchResults([]); return; } const results = []; categories.forEach((category) => { if (category && Array.isArray(category.prompts)) { category.prompts.forEach((prompt) => { if (prompt.name.toLowerCase().includes(searchTerm.toLowerCase()) || prompt.content.toLowerCase().includes(searchTerm.toLowerCase())) { results.push({ ...prompt, categoryName: category.name }); } }); } }); setSearchResults(results); }, [searchTerm, categories]); const handleAddPrompt = (prompt) => { const textarea = document.querySelector("#__next > div.sc-a2d0901c-0.gkbaSQ > div:nth-child(4) > div.sc-780ff592-0.jEOQDN > div:nth-child(3) > div:nth-child(2) > div > div:nth-child(2) > textarea"); if (textarea) { const currentValue = textarea.value; const separator = currentValue ? ", " : ""; textarea.value = currentValue + separator + prompt.content; textarea.dispatchEvent(new Event("input", { bubbles: true })); } }; return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: () => setIsOpen(!isOpen), style: { position: "fixed", right: "320px", top: "10px", background: "#1f1f1f", border: "1px solid #333", color: "#fff", padding: "4px 8px", borderRadius: "4px", cursor: "pointer", fontSize: "12px", opacity: "0.8", transition: "opacity 0.2s", zIndex: 1e3 }, onMouseOver: (e) => e.target.style.opacity = "1", onMouseOut: (e) => e.target.style.opacity = "0.8", children: isOpen ? "关闭搜索" : "搜索提示词" } ), isOpen && /* @__PURE__ */ jsxRuntimeExports.jsxs( "div", { ref: modalRef, style: { position: "fixed", top: position.y, left: position.x, background: "#1a1b1e", padding: "20px", borderRadius: "8px", boxShadow: "0 4px 12px rgba(0, 0, 0, 0.2)", zIndex: 1001, width: "400px", maxHeight: "80vh", display: "flex", flexDirection: "column", gap: "10px" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { onMouseDown: handleMouseDown, style: { padding: "8px", marginBottom: "8px", cursor: "move", borderBottom: "1px solid rgba(255, 255, 255, 0.1)", userSelect: "none" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { width: "20px", height: "2px", background: "rgba(255, 255, 255, 0.3)", margin: "0 auto" } }) } ), /* @__PURE__ */ jsxRuntimeExports.jsx( "input", { type: "text", value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), placeholder: "搜索提示词...", autoFocus: true, style: { width: "100%", padding: "8px", background: "#2c2e33", border: "1px solid #3f4147", borderRadius: "4px", color: "#fff", fontSize: "14px" } } ), /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { style: { overflowY: "auto", maxHeight: "60vh", display: "flex", flexDirection: "column", gap: "8px" }, children: searchResults.map((result, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs( "div", { style: { background: "#2c2e33", padding: "10px", borderRadius: "4px", display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: "10px" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { flex: 1 }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "14px", color: "#fff", marginBottom: "4px" }, children: result.name }), /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { fontSize: "12px", color: "rgba(255, 255, 255, 0.5)", marginBottom: "4px" }, children: [ "分类: ", result.categoryName ] }), /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "12px", color: "rgba(255, 255, 255, 0.7)", wordBreak: "break-all" }, children: result.content }) ] }), /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: () => handleAddPrompt(result), style: { background: "transparent", border: "none", color: "rgba(255, 255, 255, 0.5)", cursor: "pointer", padding: "4px 8px", fontSize: "14px" }, title: "应用到输入框", children: "←" } ) ] }, index )) } ) ] } ) ] }); }; function App() { const [isDrawerOpen, setIsDrawerOpen] = require$$0.useState(false); const [targetRight, setTargetRight] = require$$0.useState(0); const [zIndex, setZIndex] = require$$0.useState(998); const [activeTab, setActiveTab] = require$$0.useState("历史记录"); const [isTargetButtonVisible, setIsTargetButtonVisible] = require$$0.useState(false); const [categories, setCategories] = require$$0.useState(() => { const savedData = localStorage.getItem("promptCategories"); return savedData ? JSON.parse(savedData) : { "历史记录": [], "人物": [ { title: "可爱女孩", prompt: "cute girl, white hair, blue eyes, school uniform" }, { title: "帅气男孩", prompt: "handsome boy, black hair, casual wear, smile" } ], "场景": [ { title: "森林", prompt: "deep forest, sunlight through leaves, mystical atmosphere" }, { title: "城市", prompt: "modern city, night scene, neon lights, rain" } ], "风格": [ { title: "水彩画", prompt: "watercolor style, soft colors, flowing texture" }, { title: "赛博朋克", prompt: "cyberpunk style, high tech, neon, dark" } ] }; }); require$$0.useEffect(() => { localStorage.setItem("promptCategories", JSON.stringify(categories)); }, [categories]); require$$0.useEffect(() => { const handleButtonClick = () => { const textarea = document.querySelector("#__next > div.sc-a2d0901c-0.gkbaSQ > div:nth-child(4) > div.sc-780ff592-0.jEOQDN > div:nth-child(3) > div:nth-child(2) > div > div:nth-child(2) > textarea"); if (textarea && textarea.value) { const newPromptText = textarea.value; setCategories((prev) => { const historyRecords = [...prev["历史记录"]]; const existingIndex = historyRecords.findIndex((item) => item.prompt === newPromptText); if (existingIndex !== -1) { historyRecords[existingIndex] = { ...historyRecords[existingIndex], count: (historyRecords[existingIndex].count || 1) + 1, title: (/* @__PURE__ */ new Date()).toLocaleString() }; const [item] = historyRecords.splice(existingIndex, 1); historyRecords.unshift(item); } else { historyRecords.unshift({ title: (/* @__PURE__ */ new Date()).toLocaleString(), prompt: newPromptText, count: 1 }); } return { ...prev, "历史记录": historyRecords }; }); } }; const setupButtonListener = () => { const targetButton = document.querySelector("#__next > div.sc-a2d0901c-0.gkbaSQ > div:nth-child(4) > div.sc-780ff592-0.jEOQDN > div:nth-child(5) > button"); if (targetButton) { targetButton.addEventListener("click", handleButtonClick); return () => targetButton.removeEventListener("click", handleButtonClick); } return null; }; let cleanup = setupButtonListener(); const observer = new MutationObserver(() => { if (cleanup) cleanup(); cleanup = setupButtonListener(); }); observer.observe(document.body, { childList: true, subtree: true }); return () => { observer.disconnect(); if (cleanup) cleanup(); }; }, []); require$$0.useEffect(() => { const checkTargetButton = () => { const targetButton = document.querySelector("#__next > div.sc-a2d0901c-0.gkbaSQ > div:nth-child(4) > div.sc-780ff592-0.jEOQDN > div:nth-child(5) > button"); const isVisible = !!targetButton; setIsTargetButtonVisible(isVisible); if (!isVisible && isDrawerOpen) { setIsDrawerOpen(false); } }; checkTargetButton(); const observer = new MutationObserver(checkTargetButton); observer.observe(document.body, { childList: true, subtree: true }); return () => observer.disconnect(); }, [isDrawerOpen]); require$$0.useEffect(() => { const updatePosition = () => { const targetElement = document.querySelector("#__next > div.sc-a2d0901c-0.gkbaSQ > div:nth-child(4) > div.sc-780ff592-0.jEOQDN > div:nth-child(3)"); if (targetElement) { const rect = targetElement.getBoundingClientRect(); setTargetRight(rect.right); } }; const updateZIndex = () => { const upperElement = document.querySelector("#__next > div.sc-a2d0901c-0.gkbaSQ > div:nth-child(4) > div.sc-780ff592-0.jEOQDN"); if (upperElement) { const upperZIndex = parseInt(window.getComputedStyle(upperElement).zIndex) || 0; setZIndex(Math.max(1, upperZIndex - 1)); } }; updatePosition(); updateZIndex(); window.addEventListener("resize", updatePosition); const observer = new MutationObserver(() => { updatePosition(); updateZIndex(); }); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["style", "class"] }); return () => { window.removeEventListener("resize", updatePosition); observer.disconnect(); }; }, []); require$$0.useEffect(() => { const textarea = document.querySelector("#__next > div.sc-a2d0901c-0.gkbaSQ > div:nth-child(4) > div.sc-780ff592-0.jEOQDN > div:nth-child(3) > div:nth-child(2) > div > div:nth-child(2) > textarea"); if (textarea && textarea.parentElement) { const buttonContainer = document.createElement("div"); buttonContainer.style.cssText = ` display: flex; gap: 8px; position: absolute; right: 10px; top: -30px; `; const fileInput = document.createElement("input"); fileInput.type = "file"; fileInput.id = "importFile"; fileInput.accept = ".json"; fileInput.style.display = "none"; fileInput.addEventListener("change", handleImport); const importButton = document.createElement("button"); importButton.textContent = "导入提示词"; importButton.style.cssText = ` background: #1f1f1f; border: 1px solid #333; color: #fff; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; opacity: 0.8; transition: opacity 0.2s; `; importButton.onmouseover = () => importButton.style.opacity = "1"; importButton.onmouseout = () => importButton.style.opacity = "0.8"; importButton.onclick = () => fileInput.click(); const exportButton = document.createElement("button"); exportButton.textContent = "导出提示词"; exportButton.style.cssText = ` background: #1f1f1f; border: 1px solid #333; color: #fff; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; opacity: 0.8; transition: opacity 0.2s; `; exportButton.onmouseover = () => exportButton.style.opacity = "1"; exportButton.onmouseout = () => exportButton.style.opacity = "0.8"; exportButton.onclick = handleExport; buttonContainer.appendChild(fileInput); buttonContainer.appendChild(importButton); buttonContainer.appendChild(exportButton); textarea.parentElement.style.position = "relative"; textarea.parentElement.appendChild(buttonContainer); return () => { textarea.parentElement.removeChild(buttonContainer); }; } }, []); const handleAddCategory = () => { const newCategoryName = `新分类${Object.keys(categories).length + 1}`; setCategories((prev) => ({ ...prev, [newCategoryName]: [] })); setActiveTab(newCategoryName); }; const handleAddPrompt = () => { setCategories((prev) => ({ ...prev, [activeTab]: [ ...prev[activeTab], { title: "新提示词", prompt: "在此输入提示词内容" } ] })); }; const handleUpdateCategory = (oldName, newName) => { if (oldName === newName) return; if (oldName === "历史记录") return; setCategories((prev) => { const newCategories = { ...prev }; newCategories[newName] = newCategories[oldName]; delete newCategories[oldName]; return newCategories; }); if (activeTab === oldName) { setActiveTab(newName); } }; const handleUpdatePrompt = (categoryName, index, field, value) => { setCategories((prev) => ({ ...prev, [categoryName]: prev[categoryName].map( (item, i) => i === index ? { ...item, [field]: value } : item ) })); }; const handleDeleteCategory = (categoryName) => { if (categoryName === "历史记录") return; if (Object.keys(categories).length <= 2) { alert("至少保留一个分类"); return; } setCategories((prev) => { const newCategories = { ...prev }; delete newCategories[categoryName]; if (activeTab === categoryName) { setActiveTab(Object.keys(newCategories)[0]); } return newCategories; }); }; const handleDeletePrompt = (categoryName, index) => { setCategories((prev) => ({ ...prev, [categoryName]: prev[categoryName].filter((_, i) => i !== index) })); }; const handleImport = (event) => { const file = event.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (e) => { try { const importedData = JSON.parse(e.target.result); if (typeof importedData === "object" && importedData !== null) { setCategories(importedData); } else { alert("无效的数据格式"); } } catch (error) { alert("导入失败:" + error.message); } }; reader.readAsText(file); } }; const handleExport = () => { const dataStr = JSON.stringify(categories, null, 2); const dataBlob = new Blob([dataStr], { type: "application/json" }); const url = URL.createObjectURL(dataBlob); const link = document.createElement("a"); link.href = url; link.download = `prompt-categories-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); }; const handleReorderCategories = (sourceTab, targetTab) => { setCategories((prev) => { const entries = Object.entries(prev); const sourceIndex = entries.findIndex(([key]) => key === sourceTab); const targetIndex = entries.findIndex(([key]) => key === targetTab); if (sourceIndex !== -1 && targetIndex !== -1) { const newEntries = [...entries]; const [removed] = newEntries.splice(sourceIndex, 1); newEntries.splice(targetIndex, 0, removed); return Object.fromEntries(newEntries); } return prev; }); }; const handleReorderPrompts = (categoryName, sourceIndex, targetIndex) => { setCategories((prev) => { const category = [...prev[categoryName]]; const [removed] = category.splice(sourceIndex, 1); category.splice(targetIndex, 0, removed); return { ...prev, [categoryName]: category }; }); }; return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "App", children: isTargetButtonVisible && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { className: "drawer-trigger", onClick: () => setIsDrawerOpen(!isDrawerOpen), style: { position: "fixed", left: 0, top: "50%", transform: "translateY(-50%)", width: "30px", height: "60px", background: "#4a4a4a", border: "none", borderRadius: "0 4px 4px 0", cursor: "pointer", zIndex }, children: isDrawerOpen ? "←" : "→" } ), /* @__PURE__ */ jsxRuntimeExports.jsx( Drawer, { isOpen: isDrawerOpen, targetRight, zIndex, categories, activeTab, onAddCategory: handleAddCategory, onAddPrompt: handleAddPrompt, onUpdateCategory: handleUpdateCategory, onUpdatePrompt: handleUpdatePrompt, onDeleteCategory: handleDeleteCategory, onDeletePrompt: handleDeletePrompt, onTabChange: setActiveTab, onReorderCategories: handleReorderCategories, onReorderPrompts: handleReorderPrompts, onExport: handleExport } ), /* @__PURE__ */ jsxRuntimeExports.jsx( SearchPrompts, { categories: Object.entries(categories).map(([name, prompts]) => ({ name, prompts: prompts.map((p2) => ({ name: p2.title, content: p2.prompt })) })), onAddPrompt: (prompt) => { const textarea = document.querySelector("#__next > div.sc-a2d0901c-0.gkbaSQ > div:nth-child(4) > div.sc-780ff592-0.jEOQDN > div:nth-child(3) > div:nth-child(2) > div > div:nth-child(2) > textarea"); if (textarea) { textarea.value = prompt.content; textarea.dispatchEvent(new Event("input", { bubbles: true })); } } } ) ] }) }); } client.createRoot( (() => { const app = document.createElement("div"); document.body.append(app); return app; })() ).render( /* @__PURE__ */ jsxRuntimeExports.jsx(require$$0.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(App, {}) }) ); })(React, ReactDOM);