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

本贴最后更新于 238 天前,其中的信息可能已经沧海桑田

更完整的代码片段见:[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

  • 思源笔记

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

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

    26414 引用 • 109853 回帖
  • 代码片段

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

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

    206 引用 • 1488 回帖 • 1 关注

相关帖子

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    336 引用 • 324 回帖 • 1 关注
  • 思源笔记

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

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

    26414 引用 • 109852 回帖
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 506 关注
  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    229 引用 • 476 回帖
  • HBase

    HBase 是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的 Google 论文 “Bigtable:一个结构化数据的分布式存储系统”。就像 Bigtable 利用了 Google 文件系统所提供的分布式数据存储一样,HBase 在 Hadoop 之上提供了类似于 Bigtable 的能力。

    17 引用 • 6 回帖 • 71 关注
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    173 引用 • 414 回帖 • 363 关注
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖 • 4 关注
  • Thymeleaf

    Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 Velocity、 FreeMarker 等,它也可以轻易的与 Spring 等 Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用。

    11 引用 • 19 回帖 • 396 关注
  • SSL

    SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS 与 SSL 在传输层对网络连接进行加密。

    70 引用 • 193 回帖 • 408 关注
  • 倾城之链
    23 引用 • 66 回帖 • 167 关注
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 1 关注
  • 自由行
    1 关注
  • 博客

    记录并分享人生的经历。

    273 引用 • 2389 回帖
  • Access
    1 引用 • 3 回帖
  • CSDN

    CSDN (Chinese Software Developer Network) 创立于 1999 年,是中国的 IT 社区和服务平台,为中国的软件开发者和 IT 从业者提供知识传播、职业发展、软件开发等全生命周期服务,满足他们在职业发展中学习及共享知识和信息、建立职业发展社交圈、通过软件开发实现技术商业化等刚性需求。

    14 引用 • 155 回帖 • 1 关注
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    25 引用 • 254 回帖 • 1 关注
  • Openfire

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

    6 引用 • 7 回帖 • 116 关注
  • 叶归
    13 引用 • 59 回帖 • 21 关注
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    186 引用 • 1021 回帖 • 1 关注
  • V2Ray
    1 引用 • 15 回帖 • 5 关注
  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    79 引用 • 431 回帖 • 3 关注
  • RESTful

    一种软件架构设计风格而不是标准,提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    30 引用 • 114 回帖 • 9 关注
  • Swagger

    Swagger 是一款非常流行的 API 开发工具,它遵循 OpenAPI Specification(这是一种通用的、和编程语言无关的 API 描述规范)。Swagger 贯穿整个 API 生命周期,如 API 的设计、编写文档、测试和部署。

    26 引用 • 35 回帖 • 2 关注
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    12 引用 • 5 回帖 • 633 关注
  • 安全

    安全永远都不是一个小问题。

    199 引用 • 818 回帖
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    98 引用 • 367 回帖
  • 反馈

    Communication channel for makers and users.

    120 引用 • 906 回帖 • 281 关注