过早发布的尴尬失误

这是一个添加了可切换锁定功能的撰写器(composer)的用户脚本。
它还会禁用键盘快捷键,并且也适用于移动设备。

IXlc8u1sm4

https://greasyfork.org/en/scripts/566254-discourse-composer-safety-lock

添加或删除 @match 行以在你使用的论坛上启用它。

// ==UserScript==
// @name         Discourse Composer Safety Lock
// @namespace    https://meta.discourse.org
// @version      2.0
// @description  Togglable padlock on the composer to prevent accidental post submission
// @author       Canapin & AI
// @match        https://meta.discourse.org/*
// @grant        none
// @license MIT
// ==/UserScript==
(function () {
  "use strict";
 
  let locked = false;
 
  // Capture-phase listener on #reply-control to block Ctrl/Cmd/Alt+Enter
  function attachBlocker(replyControl) {
    if (replyControl._safetyLockAttached) return;
    replyControl._safetyLockAttached = true;
 
    replyControl.addEventListener(
      "keydown",
      function (e) {
        if (!locked) return;
        if (e.key === "Enter" && (e.ctrlKey || e.metaKey || e.altKey)) {
          e.stopImmediatePropagation();
          e.stopPropagation();
          e.preventDefault();
        }
      },
      true
    );
  }
 
  function updateButtons() {
    const rc = document.getElementById("reply-control");
    if (!rc) return;
    rc.querySelectorAll(".save-or-cancel .btn-primary").forEach((btn) => {
      if (locked) {
        btn.style.pointerEvents = "none";
        btn.style.filter = "grayscale(1) opacity(0.4)";
        btn.setAttribute("tabindex", "-1");
      } else {
        btn.style.pointerEvents = "";
        btn.style.filter = "";
        btn.removeAttribute("tabindex");
      }
    });
  }
 
  // Capture-phase click blocker on save button
  document.addEventListener(
    "click",
    function (e) {
      if (!locked) return;
      if (e.target.closest(".save-or-cancel .btn-primary")) {
        e.stopImmediatePropagation();
        e.stopPropagation();
        e.preventDefault();
      }
    },
    true
  );
 
  // Capture-phase keydown blocker for Enter on focused save button
  document.addEventListener(
    "keydown",
    function (e) {
      if (!locked) return;
      if (
        e.key === "Enter" &&
        e.target.closest(".save-or-cancel .btn-primary")
      ) {
        e.stopImmediatePropagation();
        e.stopPropagation();
        e.preventDefault();
      }
    },
    true
  );
 
  function injectPadlock() {
    if (document.getElementById("composer-lock-btn")) return;
    const title = document.querySelector(".composer-action-title");
    if (!title) return;
 
    const btn = document.createElement("span");
    btn.id = "composer-lock-btn";
    btn.textContent = locked ? "\u{1F512}" : "\u{1F513}";
    btn.style.cssText =
      "cursor:pointer;margin-left:10px;font-size:1.1em;user-select:none;opacity:" +
      (locked ? "1" : "0.5");
 
    btn.addEventListener("click", (e) => {
      e.preventDefault();
      e.stopPropagation();
      locked = !locked;
      btn.textContent = locked ? "\u{1F512}" : "\u{1F513}";
      btn.style.opacity = locked ? "1" : "0.5";
      updateButtons();
    });
 
    title.appendChild(btn);
    updateButtons();
  }
 
  const observer = new MutationObserver(() => {
    const rc = document.getElementById("reply-control");
    if (rc && rc.classList.contains("open")) {
      attachBlocker(rc);
      injectPadlock();
      updateButtons();
    }
  });
 
  observer.observe(document.body, {
    childList: true,
    subtree: true,
    attributes: true,
    attributeFilter: ["class"],
  });
 
  const rc = document.getElementById("reply-control");
  if (rc && rc.classList.contains("open")) {
    attachBlocker(rc);
    injectPadlock();
    updateButtons();
  }
})();
5 个赞