思源笔记块更新脚本

列举了一些常用的块更新脚本,需要配合插件块转换工具使用。

注意,本文的大多数脚本没有经过严格测试,也缺乏特殊情况的处理,只为提供参考。

测试用例

测试用例,请忽略。

//用于测试executeFunc调用(被调用的函数) const id = "20241204232140-zvlfl9t"; const lute = tools.lute; output = "---" + lute.BlockDOM2Md(lute.Md2BlockDOM(output));
//用于测试调用executeFunc const lute = tools.lute; output = "123" + lute.BlockDOM2Md(lute.Md2BlockDOM(output)); await tools .executeFunc(input, tools, output, { id: "20241204232140-zvlfl9t" }) .then((res) => { input = res.input; tools = res.tools; output = res.output; }); output = ""; input.isDelete = true;
//测试超时强制退出,修改time的值以进行比较 let time = 8000; const safePromise = new Promise((resolve) => setTimeout(() => { console.log(time); resolve(1); }, time), ); await safePromise; console.log(input);
//测试报错 const prettier = tools.prettier; const result = await prettier.prettier.format(input.block.content, { parser: "markdown", plugins: [prettier.prettierPluginMarkdown], }); a=b.c//应该在这里报错 console.log(result);

排版优化类

排版优化类的更新脚本不改变原文内容(标点符号除外),但会改变其样式,含块类型转换、行内样式转换等。

中文排版综合

  • 排版综合的代码片段将各类排版需求继续了结合,具体包含:

    • 空格替换
    • 西文字符替换为中文字符

使用 markdown 或者 dom 的 textContent 替换空格会引起格式丢失问题,所以以下代码片段使用的是逐节点遍历方法。

const dom = tools.lute.Md2BlockDOM(output); const div = document.createElement("div"); div.innerHTML = dom; const symbols = [ { en: /,/g, zh: "," }, { en: /;/g, zh: ";" }, { en: /\(/g, zh: "(" }, { en: /\)/g, zh: ")" }, { en: /!/g, zh: "!" }, { en: /(?<![0-9])\./g, zh: "。" }, //阿拉伯数字后紧跟的“.”不会被捕获 { en: /:/g, zh: ":" }, { en: /(?<![a-zA-Z]) (?![a-zA-Z])/g, zh: "" }, //空格替换,英文前后的空格不替换 { en: />/g, zh: "〉" }, { en: /</g, zh: "〈" }, { en: /\t/, zh: "" }, //在中文和英文之间增加空格 { en: /([A-Za-z]+)([\u4e00-\u9fa5]+)/g, zh: "$1 $2" }, { en: /([\u4e00-\u9fa5]+)([A-Za-z]+)/g, zh: "$1 $2" }, ]; replaceSpace(div); function replaceSpace(div) { for (let child of div.childNodes) { if (child.nodeType === Node.TEXT_NODE) { symbols.forEach((e) => { child.textContent = child.textContent.replace(e.en, e.zh); }); } replaceSpace(child); } } output = tools.lute.BlockDOM2Md(div.innerHTML);

自定义词语标注

该功能将自定义的一组词语列为重点词语,重点词语将会用粗体表示。

需要在设置中将“编辑器 -> Markdown 行级星号语法”开启。

const strongList = ["应当", "可以"]; //自定义词语 for (let item of strongList) { const reg = new RegExp(`(?<!\\*\\*)${item}(?!\\*\\*)`, "g"); output = output.replace(reg, `**${item}**`); }

通过指定分隔符号拆分段落

默认的拆分符号是 ​,按需修改即可。会将段落转换为无序列表。

const splitSymbols = [";", ":"]; //在此改变拆分符号 let markdownList = [output]; for (let symbol of splitSymbols) { markdownList = markdownList.flatMap((e) => e.split(symbol).map((e, i, arr) => { return i === arr.length - 1 ? e : e + symbol; }), ); } markdownList = markdownList.flatMap((e, i) => { if (i === 0) { return "* " + e + "\n\n"; } return " * " + e + "\n\n"; }); output = markdownList.join(``);

将一个块拆分为两个块

通过分隔符号 ;​将一个段落块拆分为多个个段落块。

output = output.split(";").join("\r\n\r\n");

使用 prettier​格式化代码

//TODO 目前仅支持JavaScript且不会判断语言类型 const prettier = tools.prettier; const result = await prettier.prettier.format(input.block.content, { parser: "babel", plugins: [prettier.prettierPluginBabel, prettier.prettierPluginEstree], }); output = ` \`\`\`js ${result} \`\`\` `;

模板类

以下代码片段类似于模板,与内置模板相比,稍微增强的地方在于:可以进行网络请求等操作、能够使用 JavaScript 的全部内置函数、语句不用使用特殊符号包裹,但是在安全性方面可能会有不足。

汇总父级反向引用

示例: ## 标题2 在这里使用该代码片段,将汇总所有链接到`标题2`的块
//为保证安全,如果在原本有内容的块上使用时,不会生效。 if (output) { return; } output = `{{SELECT * FROM blocks WHERE id IN (SELECT block_id FROM refs WHERE def_block_id='${input.block.parent_id}') ORDER BY hpath}}`;

写入今日天气

这是一段使用网络请求数据的示例,功能为在块文本后追加当日天气数据。

//删除这段语句 await tools .executeFunc(input, tools, output, { name: "高德天气API" }) .then((res) => { input = res.input; tools = res.tools; output = res.output; }); const key = input.extra.amapWeatherKey; //在这里替换为你的api Key,使用高德天气数据 const city = `110000`; //在这里设置城市代码,具体可参考https://lbs.amap.com/api/webservice/download const res = await fetch( `https://restapi.amap.com/v3/weather/ weatherInfo?key=${key}&city=${city}&extensions=base`, ); const json = await res.json(); const weather = json.lives[0].weather; output = output + `北京天气:${weather}`;

块生成类

该类更新脚本一般为将一种类型的块转化为另一种类型的块,基本原则是保留旧块,仅添加新块。其中新块的内容与原块由较大程度的不同。

列表转 mermaid 流程图

列表转 mermaid 流程图,无序列表视为分支,有序列表视为顺序执行。

示例

  • 流程开始

    • 分支 1

    • 分支 2,含有子顺序

      1. 第一步
      2. 第二步
      3. 第三步
    • 分支 3

流程开始

分支1

分支2,含有子顺序

第一步

第二步

第三步

分支3

代码

let dom = document.createElement("div"); dom.innerHTML = tools.lute.Md2BlockDOM(output); dom = dom.firstElementChild; let nodeList = []; let edgeList = []; recur(dom); function recur(dom) { if (!dom.getAttribute("data-node-id")) { return; } const id = dom.getAttribute("data-node-id"); const type = dom.getAttribute("data-type"); switch (type) { //*容器块 case "NodeList": case "NodeListItem": nodeList.push({ id, content: "", type }); let lastChild = null; const subType = dom.getAttribute("data-subtype"); for (let child of dom.children) { recur(child); const childId = child.getAttribute("data-node-id"); if (!childId) { continue; } const childType = child.getAttribute("data-type"); if (childType !== "NodeList" && childType !== "NodeListItem") { nodeList.push({ id: childId, content: child.textContent, type: childType, parentId: lastChild ? null : id, }); } if (subType === "u") { edgeList.push({ source: id, target: childId }); } else if (subType === "o") { if (lastChild) { const lastChildId = lastChild.getAttribute("data-node-id"); edgeList.push({ source: lastChildId, target: childId }); } else { edgeList.push({ source: id, target: childId }); } } lastChild = child; } break; default: return; } } //*后处理,将列表项中的第一个段落去除,并将其内容作为父级(列表项的内容) //*可以进一步将列表的第一个列表项去除,但是暂时不做处理 nodeList.map((child) => { if (!child.parentId) { return; } edgeList.forEach((e) => { if (e.source === child.id) { e.source = child.parentId; } if (e.target === child.id) { e.target = child.parentId; } }); nodeList.find((e) => e.id === child.parentId).content = child.content; }); nodeList = nodeList.filter((e) => !e.parentId); edgeList = edgeList.filter((e) => e.source !== e.target); let mermaid = nodeList.reduce((pre, cur) => { let content = tools.lute.BlockDOM2Md(cur.content).replace(/\{:.*?\}/g, ""); content = content.replace(/[\r\n|\r|\n]{2,}/, ""); return pre + `${cur.id}["\`${content}\`"]` + "\r\n"; }, ""); mermaid = mermaid + edgeList.reduce((pre, cur) => { return pre + `${cur.source} --> ${cur.target}` + "\r\n"; }, ""); mermaid = ` \`\`\`mermaid flowchart TD ${mermaid} \`\`\` `; output = output + "\r\n\r\n" + mermaid;

特殊需求类

该类更新脚本为满足个性化需求,并不通用,仅供参考。

法条更新(合并)

const reg = /(第.{1,7}?条)/; if (output.search(reg) !== 0) { input.isDelete = true; } else { let i = input.index; let text = input.array[i].markdown; let result = ""; do { text = text.replace(/[\u3000\t ]/g, ""); if (!text) { continue; } //如不需要对第一行包含`第xx条`则对其进行加粗,改为let textResult = text; let textResult = i !== input.index ? text : text.replace(reg, "**$1** "); //对以`(`开头的行进行缩进 if (text.startsWith("(") || text.startsWith("(")) { textResult = " * " + textResult; } else { textResult = "* " + textResult; } result += "\r\n" + textResult; i++; text = input.array[i] ? input.array[i].markdown : null; } while (text && text.search(reg) !== 0); result = result + "\r\n---\r\n"; //最后添加分隔线 output = result; input.extra.attrs = { name: "", alias: "" }; //删除所有命名和别名 }

法条更新

在原有段落转换为列表上做了增强,会将用软回车分行的文本(即在一个块中的多行文本)转换为列表,同时有一些特异性,包括:第一行包含 第xx条​则对其进行加粗,含 (xx)​的行会缩进。

以下是输入示例:

第二百二十条 有下列情形之一的,当事人可以向人民检察院申请检察建议或者抗诉:
(一)人民法院驳回再审申请的;
(二)人民法院逾期未对再审申请作出裁定的;
(三)再审判决、裁定有明显错误的。
人民检察院对当事人的申请应当在三个月内进行审查,作出提出或者不予提出检察建议或者抗诉的决定。当事人不得再次向人民检察院申请检察建议或者抗诉。

以下是输出示例

  • 第二百二十条 有下列情形之一的,当事人可以向人民检察院申请检察建议或者抗诉:

    • (一)人民法院驳回再审申请的;
    • (二)人民法院逾期未对再审申请作出裁定的;
    • (三)再审判决、裁定有明显错误的。
  • 人民检察院对当事人的申请应当在三个月内进行审查,作出提出或者不予提出检察建议或者抗诉的决定。当事人不得再次向人民检察院申请检察建议或者抗诉。


if (input.block.type == "p") { const list = output.split("\n"); let result = ""; let i = 0; for (let text of list) { text = text.replace(/[\u3000\t ]/g, ""); if (!text) { continue; } //如不需要对第一行包含`第xx条`则对其进行加粗,改为let textResult = text; let textResult = i ? text : text.replace(/(第.{1,6}条)/, "**$1** "); if (text.startsWith("*")) { textResult = textResult.replace("*", "* "); } else { textResult = "* " + textResult; } //对以`(`开头的行进行缩进 if (text.startsWith("(") || text.startsWith("(")) { textResult = " " + textResult; } result += "\r\n" + textResult; i++; } output = result + "\r\n---\r\n"; //最后添加分隔线 //移除属性 input.extra.attrs.name = ""; input.extra.attrs.alias = ""; }

法条自动链接(未适配 v0.4.0)

拟进行更新的块: 《刑事诉讼法》第50条规定,可以用于证明案件事实的材料,都是证据。包括:…… 拟进行的链接的块: 文档名为《刑事诉讼法》 * **第五十条** 可以用于证明案件事实的材料,都是证据。 * 证据包括: * (一)物证;…… * 证据必须经过查证属实,才能作为定案的根据。 更新后的效果: ((20230315001689-zoh0qlk "《刑事诉讼法》第50条"))规定,可以用于证明案件事实的材料,都是证据。包括:……
//* 在该块中查找可以链接的文本 const matchGroup = input.block.content.match(/(《.*?》)第(.*?)条/); if (matchGroup) { const docTitle = matchGroup[1]; const num = num2Chinese(matchGroup[2]); //*查询法条所在文档 let body = { stmt: `SELECT * FROM blocks WHERE content='${docTitle}' AND type='d'`, }; let res = await fetch("http://127.0.0.1:6806/api/query/sql", { body: JSON.stringify(body), method: "POST", }); const docId = await res.json().then((e) => e.data[0].id); //* 查询法条所在块 body = { stmt: `SELECT * FROM blocks WHERE root_id='${docId}' AND content LIKE ' 第${num}%' AND type='l'`, }; res = await fetch("http://127.0.0.1:6806/api/query/sql", { body: JSON.stringify(body), method: "POST", }); const blockId = await res.json().then((e) => e.data[0].id); //* 生成链接并更新块 const link = `((${blockId} "${matchGroup[0]}"))`; output = output.replace(matchGroup[0], link); } //数字转中文,仅支持到千,对于法条来说够用了 function num2Chinese(num) { const chnNumChar = [ "零", "一", "二", "三", "四", "五", "六", "七", "八", "九", ]; const unit = ["千", "百", "十", ""]; const unitLength = 4; const text = num + ""; const list = text.split(""); while (list.length < 4) { list.unshift("0"); } let subResultList = []; for (let i = list.length - 1; i >= 0; i--) { if (list[i] !== "0") { subResultList.unshift(chnNumChar[list[i]] + unit[i]); } else { subResultList.unshift("零"); } } let result = subResultList.join(""); //*去除多余的零 while (result.search("零零") !== -1) { result = result.replace(/零零/g, "零"); } result = result.replace(/^零/, ""); result = result.replace(/零$/, ""); return result; }
  • 思源笔记

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

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

    24834 引用 • 102158 回帖

相关帖子

欢迎来到这里!

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

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