抖音:一键开启设计模式以解除选中/复制限制

只在 https://www.douyin.com/* 生效。默认关闭。点击面板一键切换 document.designMode(设计模式),使页面所有内容可选/可复制。使用后建议关闭或刷新页面以恢复正常交互。

// ==UserScript==
// @name         抖音:一键开启设计模式以解除选中/复制限制
// @namespace    https://toolsdar.cn/
// @version      1.0
// @description  只在 https://www.douyin.com/* 生效。默认关闭。点击面板一键切换 document.designMode(设计模式),使页面所有内容可选/可复制。使用后建议关闭或刷新页面以恢复正常交互。
// @match        https://www.douyin.com/*
// @grant        none
// @license MIT
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  // ====== UI 创建 ======
  function createPanel() {
    if (document.getElementById('tm-designmode-panel')) return;
    const panel = document.createElement('div');
    panel.id = 'tm-designmode-panel';
    panel.style.cssText = [
      'position:fixed',
      'right:12px',
      'bottom:12px',
      'z-index:2147483647',
      'font-family:system-ui,-apple-system,Segoe UI,Roboto,"Helvetica Neue",Arial',
      'background:rgba(10,10,10,0.7)',
      'color:#fff',
      'padding:8px',
      'border-radius:10px',
      'display:flex',
      'gap:8px',
      'align-items:center',
      'box-shadow:0 6px 18px rgba(0,0,0,0.3)'
    ].join(';');

    panel.innerHTML = `
      <button id="tm-designmode-toggle" style="background:#fff;color:#000;border:0;padding:6px 10px;border-radius:6px;cursor:pointer;font-weight:600">开启设计模式</button>
      <div id="tm-designmode-status" style="font-size:12px;opacity:0.95">已关闭</div>
    `;
    document.documentElement.appendChild(panel);

    document.getElementById('tm-designmode-toggle').addEventListener('click', toggleDesignMode);
    panel.addEventListener('mousedown', startDrag);
  }

  function startDrag(e) {
    const panel = e.currentTarget;
    let startX = e.clientX, startY = e.clientY;
    const rect = panel.getBoundingClientRect();
    const offsetX = startX - rect.left, offsetY = startY - rect.top;
    function onMove(ev) {
      panel.style.left = Math.max(6, ev.clientX - offsetX) + 'px';
      panel.style.top = Math.max(6, ev.clientY - offsetY) + 'px';
      panel.style.right = 'auto';
      panel.style.bottom = 'auto';
    }
    function onUp() {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
    }
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onUp);
  }

  // ====== 设计模式控制 ======
  function setDesignMode(on) {
    try {
      document.designMode = on ? 'on' : 'off';
      try { document.body && document.body.focus && document.body.focus(); } catch (e) {}
      updatePanel();
      showToast(on ? '设计模式已开启:现在可选中并复制页面所有内容(完成后请关闭或刷新)' : '设计模式已关闭(已恢复)', 2600);
    } catch (e) {
      console.error('tm: setDesignMode error', e);
      showToast('无法切换设计模式(浏览器限制)', 2000);
    }
  }

  function toggleDesignMode() {
    const isOn = (document.designMode && document.designMode.toLowerCase() === 'on');
    setDesignMode(!isOn);
  }

  function updatePanel() {
    const btn = document.getElementById('tm-designmode-toggle');
    const status = document.getElementById('tm-designmode-status');
    if (!btn || !status) return;
    const isOn = (document.designMode && document.designMode.toLowerCase() === 'on');
    if (isOn) {
      btn.textContent = '关闭设计模式';
      btn.style.background = '#ffc107';
      btn.style.color = '#000';
      status.textContent = '已开启';
    } else {
      btn.textContent = '开启设计模式';
      btn.style.background = '#fff';
      btn.style.color = '#000';
      status.textContent = '已关闭';
    }
  }

  // ====== 简单 toast 提示 ======
  let toastTimer = null;
  function showToast(msg, ms = 1800) {
    try {
      const id = 'tm-designmode-toast';
      let t = document.getElementById(id);
      if (!t) {
        t = document.createElement('div');
        t.id = id;
        t.style.cssText = 'position:fixed;right:12px;bottom:84px;z-index:2147483647;background:rgba(0,0,0,0.7);color:#fff;padding:8px 12px;border-radius:8px;font-size:13px;backdrop-filter:blur(4px)';
        document.documentElement.appendChild(t);
      }
      t.textContent = msg;
      t.style.opacity = '1';
      if (toastTimer) clearTimeout(toastTimer);
      toastTimer = setTimeout(() => { try { t.style.opacity = '0'; } catch (e) {} }, ms);
    } catch (e) {}
  }

  // ====== 初始化 ======
  function init() {
    createPanel();
    updatePanel();
    setInterval(updatePanel, 800); // 保持面板与 designMode 同步
  }

  if (document.readyState === 'complete' || document.readyState === 'interactive') {
    setTimeout(init, 120);
  } else {
    document.addEventListener('DOMContentLoaded', init, { once: true });
  }

})();