列举了一些常用的块更新脚本,需要配合插件块转换工具使用。
注意,本文的大多数脚本没有经过严格测试,也缺乏特殊情况的处理,只为提供参考。
测试用例
测试用例,请忽略。
//用于测试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,含有子顺序
- 第一步
- 第二步
- 第三步
-
分支 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;
}
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于