novalai prompt editor

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);