copy-notion-page-content-as-markdown

复制 Notion Page 内容为标准 Markdown 文本。

目前为 2023-10-11 提交的版本。查看 最新版本

// ==UserScript==
// @name         copy-notion-page-content-as-markdown
// @name:en Copy Notion Page Content AS Markdown
// @name:zh-CN 复制 Notion Page 内容为标准 Markdown 文本
// @description  复制 Notion Page 内容为标准 Markdown 文本。
// @description:zh-CN  复制 Notion Page 内容为标准 Markdown 文本。
// @description:en Copy Notion Page Content AS Markdown.
// @namespace    https://blog.diqigan.cn
// @version      0.1.2
// @license MIT
// @author       Seven
// @match        *://www.notion.so/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=notion.so
// @grant        GM_setClipboard
// ==/UserScript==

(function () {
  'use strict';

  init();

  /**
   * 初始化动作
   */
  function init() {
    waitFor('#notion-app .notion-page-content').then(([notionContentElement]) => {
      initCopyButton();
    });
  }

  /**
   * 初始化复制按钮
   */
  function initCopyButton() {
    let copyButton = document.createElement('div');

    copyButton.style.position = "fixed";
    copyButton.style.width = "88px";
    copyButton.style.height = "22px";
    copyButton.style.lineHeight = "22px";
    copyButton.style.top = "14%";
    copyButton.style.right = "1%";
    copyButton.style.background = "#0084ff";
    copyButton.style.fontSize = "14px";
    copyButton.style.color = "#fff";
    copyButton.style.textAlign = "center";
    copyButton.style.borderRadius = "6px";
    copyButton.style.zIndex = 10000;
    copyButton.style.cursor = "pointer";
    copyButton.style.opacity = 0.6;
    copyButton.innerHTML = "Copy Content";

    copyButton.addEventListener('click', copyPageContentAsync);
    console.log('initCopyButton');
    document.body.prepend(copyButton);
  }

  /**
   * 复制 Notion Page 内容
   */
  async function copyPageContentAsync() {
    await copyElementAsync('#notion-app .notion-page-content');

    const clipboardContent = await readClipboard();
    if (!clipboardContent) {
      showMessage('复制失败');
      return;
    }

    console.log('clipboardContent', clipboardContent);
    const markdownContent = fixMarkdownFormat(clipboardContent);
    console.log('markdown', markdownContent);

    GM_setClipboard(markdownContent);
    showMessage('复制成功');
  }

  /**
   * 修正 markdown 格式
   */
  function fixMarkdownFormat(markdown) {
    if (!markdown) {
      return;
    }

    // 给没有 Caption 的图片添加 ALT 文字
    return markdown.replaceAll(/\!(http.*\.\w+)/g, (match, group1) => {
      const processedText = decodeURIComponent(group1);
      console.log('regex', processedText);
      return `![picture](${processedText})`;
    });

    // TODO 给有 Caption 的图片去除多余文字
  }

  /**
   * 复制 DOM 元素(在 DOM 元素上执行复制操作)
   */
  async function copyElementAsync(selector) {
    const pageContent = document.querySelector(selector);
    pageContent.focus();

    let range = document.createRange();
    range.selectNodeContents(pageContent);

    let selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(range);

    await sleep(300);
    document.execCommand('copy');
    await sleep(200);
    selection.removeAllRanges();
  }

  /**
   * 在页面显示提示信息
   */
  function showMessage(message) {
    const toast = document.createElement('div');
    toast.style.position = 'fixed';
    toast.style.bottom = '20px';
    toast.style.left = '50%';
    toast.style.transform = 'translateX(-50%)';
    toast.style.padding = '10px 20px';
    toast.style.background = 'rgba(0, 0, 0, 0.8)';
    toast.style.color = 'white';
    toast.style.borderRadius = '5px';
    toast.style.zIndex = '9999';
    toast.innerText = message;
    document.body.appendChild(toast);
    setTimeout(function () {
      toast.remove();
    }, 3000);
  }

  /**
   * 读取系统剪切板内容
   */
  async function readClipboard() {
    try {
      const clipText = await navigator.clipboard.readText();
      return clipText;
    } catch (error) {
      console.error('Failed to read clipboard:', error);
    }
  }

  /**
   * 等待指定 DOM 元素加载完成之后再执行方法
   */
  function waitFor(...selectors) {
    return new Promise(resolve => {
      const delay = 500;
      const f = () => {
        const elements = selectors.map(selector => document.querySelector(selector));
        if (elements.every(element => element != null)) {
          resolve(elements);
        } else {
          setTimeout(f, delay);
        }
      }
      f();
    });
  }

  /**
   * 延迟执行
   **/
  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
})();

QingJ © 2025

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