Quick Edit

Popup chỉnh sửa nhanh nội dung bôi đen với giao diện tông màu tối (dark mode)

目前為 2025-06-18 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Quick Edit
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  Popup chỉnh sửa nhanh nội dung bôi đen với giao diện tông màu tối (dark mode)
// @author       DuyNguyen2k6
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
  function showNotification(message, type = "error") {
    const notification = document.createElement("div");
    notification.style.position = "fixed";
    notification.style.top = "10px";
    notification.style.right = "10px";
    notification.style.padding = "10px";
    notification.style.background = type === "error" ? "#f44336" : "#4caf50";
    notification.style.color = "white";
    notification.style.borderRadius = "4px";
    notification.style.zIndex = "10000";
    notification.innerText = message;
    document.body.appendChild(notification);
    setTimeout(() => notification.remove(), 3000);
  }

  function sanitizeText(text) {
    const div = document.createElement("div");
    div.textContent = text;
    return div.textContent;
  }

  function saveEditHistory(originalText, newText) {
    if (!window.chrome || !chrome.storage) return;
    chrome.storage.local.get({ editHistory: [] }, (data) => {
      const history = data.editHistory || [];
      history.push({
        timestamp: new Date().toISOString(),
        original: originalText,
        edited: newText,
      });
      if (history.length > 50) history.shift();
      chrome.storage.local.set({ editHistory: history });
    });
  }

  function replaceTextInRange(range, newText) {
    const startNode = range.startContainer;
    const endNode = range.endContainer;
    const startOffset = range.startOffset;
    const endOffset = range.endOffset;

    if (startNode === endNode && startNode.nodeType === Node.TEXT_NODE) {
      const originalText = startNode.textContent;
      startNode.textContent =
        originalText.substring(0, startOffset) +
        newText +
        originalText.substring(endOffset);
    } else {
      const commonAncestor = range.commonAncestorContainer;
      const walker = document.createTreeWalker(
        commonAncestor,
        NodeFilter.SHOW_TEXT,
        {
          acceptNode: (node) =>
            range.intersectsNode(node)
              ? NodeFilter.FILTER_ACCEPT
              : NodeFilter.FILTER_SKIP,
        }
      );

      let node;
      const nodesToUpdate = [];
      while ((node = walker.nextNode())) {
        nodesToUpdate.push(node);
      }

      nodesToUpdate.forEach((textNode, index) => {
        if (index === 0) {
          textNode.textContent =
            textNode.textContent.substring(0, startOffset) + newText;
        } else if (index === nodesToUpdate.length - 1) {
          textNode.textContent = textNode.textContent.substring(endOffset);
        } else {
          textNode.textContent = "";
        }
      });

      nodesToUpdate.forEach((node) => {
        if (node.textContent === "" && node.parentNode) {
          node.parentNode.removeChild(node);
        }
      });
    }
  }

  function createEditorPopup(selectedText, range) {
    const existing = document.getElementById("html-quick-edit-popup");
    if (existing) existing.remove();

    const popup = document.createElement("div");
    popup.id = "html-quick-edit-popup";

    Object.assign(popup.style, {
      position: "fixed",
      zIndex: "10000",
      background: "#121212",
      color: "#eee",
      border: "1px solid #444",
      padding: "10px",
      minWidth: "350px",
      maxWidth: "90vw",
      boxShadow: "0 2px 12px rgba(0,0,0,0.9)",
      borderRadius: "8px",
      fontFamily: "Arial, sans-serif",
      fontSize: "14px",
      left: "50%",
      top: "50%",
      transform: "translate(-50%, -50%)",
      userSelect: "text",
      display: "flex",
      flexDirection: "column",
    });

    const header = document.createElement("div");
    header.textContent = "Quick Edit by DuyNguyen2k6";
    Object.assign(header.style, {
      fontWeight: "bold",
      fontSize: "16px",
      marginBottom: "8px",
      textAlign: "center",
      cursor: "move",
      userSelect: "none",
      color: "#ddd",
    });
    popup.appendChild(header);

    const textarea = document.createElement("textarea");
    textarea.value = selectedText;
    Object.assign(textarea.style, {
      width: "100%",
      height: "120px",
      padding: "8px",
      fontSize: "14px",
      fontFamily: "inherit",
      boxSizing: "border-box",
      resize: "vertical",
      border: "1px solid #555",
      outline: "none",
      background: "#1e1e1e",
      color: "#eee",
      transition: "background-color 0.3s ease",
      marginBottom: "8px",
    });
    textarea.oninput = () => {
      textarea.style.backgroundColor = "#333322";
      setTimeout(() => {
        textarea.style.backgroundColor = "#1e1e1e";
      }, 300);
    };
    textarea.onkeydown = (e) => {
      if (e.key === "Tab") {
        e.preventDefault();
        const start = textarea.selectionStart;
        const end = textarea.selectionEnd;
        textarea.value =
          textarea.value.substring(0, start) + "\n" + textarea.value.substring(end);
        textarea.selectionStart = textarea.selectionEnd = start + 1;
      }
    };
    popup.appendChild(textarea);

    const guide = document.createElement("div");
    guide.textContent = "Press Enter to save, Escape to cancel, Tab for new line.";
    Object.assign(guide.style, {
      marginBottom: "8px",
      color: "#aaa",
    });
    popup.appendChild(guide);

    popup.onkeydown = (e) => {
      if (e.key === "Enter" && !e.shiftKey) {
        e.preventDefault();
        try {
          const cleanedText = sanitizeText(textarea.value);
          replaceTextInRange(range, cleanedText);
          saveEditHistory(selectedText, cleanedText);
          popup.remove();
          showNotification("Changes saved!", "success");
        } catch (error) {
          showNotification("Error saving: " + error.message);
          console.error("Save error:", error);
        }
      } else if (e.key === "Escape") {
        popup.remove();
      }
    };

    let isDragging = false,
      currentX = 0,
      currentY = 0,
      initialX = 0,
      initialY = 0;

    header.addEventListener("mousedown", (e) => {
      isDragging = true;
      initialX = e.clientX - currentX;
      initialY = e.clientY - currentY;
      document.addEventListener("mousemove", onMouseMove);
      document.addEventListener("mouseup", onMouseUp);
    });

    function onMouseMove(e) {
      if (isDragging) {
        e.preventDefault();
        currentX = e.clientX - initialX;
        currentY = e.clientY - initialY;
        const popupRect = popup.getBoundingClientRect();
        const maxX = window.innerWidth - popupRect.width;
        const maxY = window.innerHeight - popupRect.height;
        currentX = Math.max(0, Math.min(currentX, maxX));
        currentY = Math.max(0, Math.min(currentY, maxY));
        popup.style.left = `${currentX}px`;
        popup.style.top = `${currentY}px`;
        popup.style.transform = "none";
      }
    }

    function onMouseUp() {
      isDragging = false;
      document.removeEventListener("mousemove", onMouseMove);
      document.removeEventListener("mouseup", onMouseUp);
    }

    document.body.appendChild(popup);

    textarea.focus();
  }

  document.addEventListener("mousedown", (e) => {
    if (e.button === 1) { // Middle click
      const selection = window.getSelection();
      if (selection && !selection.isCollapsed) {
        e.preventDefault();
        const range = selection.getRangeAt(0);
        const selectedText = selection.toString();
        createEditorPopup(selectedText, range);
      }
    }
  });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址