[js] 用代码片段让思源笔记发布到微信公众号支持公式渲染

更完整的代码片段见:[js] 思源笔记粘贴到微信公众号处理公式、链接、多级列表 - 链滴


最近沉迷统计学,打算在微信公众号发统计分析的笔记,但是思源笔记不支持粘贴公式到微信公众号,行内公式和块级公式都不行,非常苦恼

之前提了这个 issue,发布到微信公众号支持公式渲染 · Issue #12571 · siyuan-note/siyuan,等 DV 两口子解决也是遥遥无期

只好自己尝试解决了,因为正好知道微信 Markdown 编辑器 | Doocs 开源社区这个编辑器是支持 markdown 公式粘贴到微信公众号,于是扒代码,把代码发给 GPT 如何改进思源笔记的预览模式……花了一小时

竟然真的给我问出来了哈哈哈

代码片段

(async () => {
    // 定义观察DOM变化的函数
    function observeDomChange(targetNode, callback) {
        const config = { childList: true, subtree: true };
        const observer = new MutationObserver((mutationsList) => {
            for (const mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    callback(mutation);
                }
            }
        });
        observer.observe(targetNode, config);
        return observer;
    }

    const targetNode = document.body;

    // 开始观察DOM变化,并在检测到.color__square元素时运行updateColors
    observeDomChange(targetNode, async (mutation) => {
        const hasMathBlock = mutation.target.querySelector('.layout__wnd--active .protyle:not(.fn__none) .protyle-preview>.b3-typography div[data-subtype="math"]:not([data-repleaced="true"])');
        const hasInlineMath = mutation.target.querySelector('.layout__wnd--active .protyle:not(.fn__none) .protyle-preview>.b3-typography span[data-type="inline-math"]:not([data-repleaced="true"])');
        if (mutation.target.classList.contains("protyle-preview")) {
            if (hasMathBlock || hasInlineMath) {
                await fetchSyncPost('/api/notification/pushMsg', {
                    "msg": "正在处理公式",
                    "timeout": 5000
                });
                loadMathJax(async () => {
                    await Promise.all([renderMathBlocks(), renderInlineMath()]);
                    console.log('MathJax has been loaded!');
                    await fetchSyncPost('/api/notification/pushMsg', {
                        "msg": "公式处理完毕",
                        "timeout": 5000
                    });
                });
            }
        }
    });

    function createRenderer(display) {
        return (mathContent) => {
            const cleanedContent = mathContent.replace(/\\displaystyle\s*{([^}]*)}/g, '$1');

            window.MathJax.texReset();
            const mjxContainer = window.MathJax.tex2svg(cleanedContent, { display });
            const svg = mjxContainer.firstChild;
            const width = svg.style[`min-width`] || svg.getAttribute(`width`);
            svg.removeAttribute(`width`);
            svg.style = `max-width: 70vw !important;`;
            svg.style.width = width;
            svg.style.display = `initial`;
            if (display) {
                return `<section style="box-sizing: border-box; border-width: 0px; border-style: solid; border-color: hsl(var(--border)); user-select: text !important; color: rgb(10, 10, 10); font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; text-align: center; overflow: auto;">${svg.outerHTML}</section>`;
            }
            return svg.outerHTML;
        };
    }

    async function renderMathBlocks() {
        const mathBlocks = document.querySelectorAll('.b3-typography div[data-subtype="math"]:not([data-repleaced="true"])');
        const renderBlock = createRenderer(true);

        return Promise.all(Array.from(mathBlocks).map(async (block) => {
            const mathContent = block.getAttribute('data-content');
            try {
                block.innerHTML = renderBlock(mathContent);
                block.setAttribute('data-repleaced', 'true');
            } catch (error) {
                console.error('Error rendering MathJax block:', error);
            }
        }));
    }

    async function renderInlineMath() {
        const inlineMaths = document.querySelectorAll('span[data-type="inline-math"]:not([data-repleaced="true"])');
        const renderInline = createRenderer(false);

        return Promise.all(Array.from(inlineMaths).map(async (span) => {
            const mathContent = span.getAttribute('data-content');
            try {
                span.innerHTML = renderInline(mathContent);
                span.style.verticalAlign = 'middle';
                span.style.lineHeight = '1';
                span.setAttribute('data-repleaced', 'true');
            } catch (error) {
                console.error('Error rendering MathJax inline:', error);
            }
        }));
    }

    function loadMathJax(callback) {
        window.MathJax = {
            tex: {
                inlineMath: [['$', '$'], ['\\(', '\\)']]
            },
            svg: {
                fontCache: 'none'
            }
        };

        const script = document.createElement('script');
        script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';
        script.async = true;
        script.onload = callback;
        document.head.appendChild(script);
    }

    async function fetchSyncPost(url, data, returnType = 'json') {
        const init = {
            method: "POST",
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data)
        };
        try {
            const res = await fetch(url, init);
            return returnType === 'json' ? await res.json() : await res.text();
        } catch (e) {
            console.log(e);
            return returnType === 'json' ? { code: e.code || 1, msg: e.message || "", data: null } : "";
        }
    }
})();


预览

没处理前:块公式惨不忍睹,行内公式有些看着正常,但是手机一预览一旦有下标、上标就会跑到其他地方去

PixPin20241114001310.png

代码片段运行后:行内公式和公式块都以 svg 格式插入公式,一切正常

PixPin20241114001220.png

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    23005 引用 • 92532 回帖
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    90 引用 • 561 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
Achuan-2
给时间以生命而不是给生命以时间,如果你喜欢我的分享,欢迎给我买杯咖啡 https://www.yuque.com/achuan-2 上海

推荐标签 标签

  • ReactiveX

    ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的 API。它组合了观察者模式,迭代器模式和函数式编程的优秀思想。

    1 引用 • 2 回帖 • 161 关注
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖 • 1 关注
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    247 引用 • 1348 回帖
  • CongSec

    本标签主要用于分享网络空间安全专业的学习笔记

    1 引用 • 1 回帖 • 16 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖 • 1 关注
  • WebSocket

    WebSocket 是 HTML5 中定义的一种新协议,它实现了浏览器与服务器之间的全双工通信(full-duplex)。

    48 引用 • 206 回帖 • 319 关注
  • 音乐

    你听到信仰的声音了么?

    61 引用 • 511 回帖
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 672 关注
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    15 引用 • 7 回帖
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    313 引用 • 547 回帖
  • Maven

    Maven 是基于项目对象模型(POM)、通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。

    186 引用 • 318 回帖 • 281 关注
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 559 关注
  • ZooKeeper

    ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 HBase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    59 引用 • 29 回帖 • 14 关注
  • 资讯

    资讯是用户因为及时地获得它并利用它而能够在相对短的时间内给自己带来价值的信息,资讯有时效性和地域性。

    55 引用 • 85 回帖 • 1 关注
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖 • 2 关注
  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    52 引用 • 190 回帖 • 1 关注
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    135 引用 • 190 回帖
  • 安装

    你若安好,便是晴天。

    132 引用 • 1184 回帖 • 3 关注
  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    192 引用 • 291 回帖 • 370 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 76 关注
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖 • 6 关注
  • TGIF

    Thank God It's Friday! 感谢老天,总算到星期五啦!

    288 引用 • 4485 回帖 • 664 关注
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 584 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖 • 4 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    36 引用 • 37 回帖 • 535 关注
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 101 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖 • 1 关注