实现 SiYuan 笔记图、表和公式的自动编号和交叉引用

本贴最后更新于 740 天前,其中的信息可能已经时异事殊

对图、表和公式的自动编号和交叉引用是科研笔记最基本的功能,但当前各种笔记系统均未实现该功能。因此,抽出一天时间,利用 Quicker 并结合 C#代码,针对 Siyuan 做了一下尝试。虽然还有待完善,但也基本实现了该功能,见图 11

交叉引用 - 动作信息 - Quicker (getquicker.net)

主要功能:

  1. 实现对图进行自动编号,同时增减图刷新后,编号将进行全自动更新;

  2. 实现对表进行自动编号,同时增减表刷新后,编号将进行全自动更新;

  3. 实现对公式进行自动编号,同时增减公式刷新后,编号将进行全自动更新;

  4. 实现了对图表和公式的交叉引用,同时增删图表和公式后将自动更新交叉引用

image

图 1 界面

主要代码如下:

using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; public static string Exec(string paramValue) { JObject Jobj=JObject.Parse(paramValue); string block_id = (string)Jobj["block_id"]; string API_token= (string)Jobj["API_token"]; string cmd = (string)Jobj["cmd"]; string data = (string)Jobj["data"]; if (cmd.ToLower()=="setcaption"){ SetCaption(data, block_id,API_token); } else if (cmd.ToLower()=="clear"){ ClearCaptionInfo(block_id,API_token); } else if (cmd.ToLower()=="update") { UpdateCaptions(block_id,API_token); } return ""; } private static Dictionary<string, string> ial2JSON(string ial, string mk) { Dictionary<string, string> mydict = new Dictionary<string, string>(); foreach (Match m in Regex.Matches(ial, "([\\w-]+)=\"([^\"]*)\"")) { mydict.Add(m.Groups[1].Value, m.Groups[2].Value); } mydict.Add("mk", mk); return mydict; } public static string ClearCaptionInfo(string block_id, string Token) { SYAPI siyuanAPI = new SYAPI(Token); JObject myJobj = new JObject(); myJobj["custom-type"] = ""; myJobj["custom-id"] = ""; myJobj["name"] = ""; siyuanAPI.SetBlockAttrs(block_id, myJobj); siyuanAPI.PushMsg("已完成更新!"); return ""; } public static string SetCaption(string CaptionLabel, string block_id, string Token) { SYAPI siyuanAPI = new SYAPI(Token); JObject result=siyuanAPI.SQL_query(string.Format("Select markdown from blocks where id='{0}'", block_id)); string cmk = (string)result["data"][0]["markdown"]; Dictionary<string, string> cdict = new Dictionary<string, string>() { { "custom-type",CaptionLabel}, { "custom-id","1"}, { "name",CaptionLabel+" 1"}, { "id",block_id}, { "mk",cmk} }; string doc_id = siyuanAPI.GetDocumentIDbyBlockID(block_id); result = siyuanAPI.SQL_query(string.Format("select markdown,ial from blocks where id in (SELECT block_id FROM attributes where name='custom-type' and root_id='{0}') ", doc_id)); List<Dictionary<string, string>> data = result["data"].Select(x => ial2JSON((string)x["ial"], (string)x["markdown"])).ToList<Dictionary<string, string>>(); data.Add(cdict); result = siyuanAPI.GetChildBlocks(doc_id); List<string> idx = result["data"].Select(x => (string)x["id"]).ToList<string>(); data = data.OrderBy(mdict => idx.IndexOf(mdict["id"])).ToList(); Dictionary<string, int> CaptionDictID = new Dictionary<string, int>(); foreach (Dictionary<string, string> item in data) { string captionLabel = item["custom-type"]; int seqID = int.Parse(item["custom-id"]); string id = item["id"]; string name = item["name"]; string mk = item["mk"]; if (CaptionDictID.Keys.Contains(captionLabel)) { CaptionDictID[captionLabel] += 1; } else { CaptionDictID[captionLabel] = 1; } if ((new string[] { "式", "equation", "eq", "eq.", "公式" }).Contains(captionLabel.ToLower())) { mk = Regex.Replace(mk, "^\\$\\$", ""); mk = Regex.Replace(mk, "\\$\\$$", ""); mk = Regex.Replace(mk.Trim(), @"\\tag ?\{ ?(\d+-)?\d+ ?\}", ""); name = captionLabel + " " + CaptionDictID[captionLabel].ToString(); siyuanAPI.UpdateBlock(id, string.Format("$${0}\\tag{{{1}{2}}}$$", mk.Trim(), "", CaptionDictID[captionLabel])); } else { string pattern = string.Format("^(?<header>[#\\* ]*)(?<CaptionLabel>{0} \\d{{1,3}})?(?<last>.*)$", captionLabel); GroupCollection groups = Regex.Match(mk, pattern).Groups; mk = groups["header"].ToString().Trim() + captionLabel + " " + CaptionDictID[captionLabel].ToString() + " " + groups["last"].ToString().Trim(); siyuanAPI.UpdateBlock(id, mk); } JObject myJobj = new JObject(); myJobj["custom-type"] = captionLabel; myJobj["custom-id"] = CaptionDictID[captionLabel].ToString(); myJobj["name"] = captionLabel + " " + CaptionDictID[captionLabel].ToString(); siyuanAPI.SetBlockAttrs(id, myJobj); } return ""; } public static void UpdateCaptions(string block_id, string Token) { SYAPI siyuanAPI = new SYAPI(Token); string doc_id = siyuanAPI.GetDocumentIDbyBlockID(block_id); JObject result = siyuanAPI.SQL_query(string.Format("select markdown,ial from blocks where id in (SELECT block_id FROM attributes where name='custom-type' and root_id='{0}') ", doc_id)); List<Dictionary<string, string>> data = result["data"].Select(x => ial2JSON((string)x["ial"], (string)x["markdown"])).ToList<Dictionary<string, string>>(); result = siyuanAPI.GetChildBlocks(doc_id); List<string> idx = result["data"].Select(x => (string)x["id"]).ToList<string>(); data = data.OrderBy(mdict => idx.IndexOf(mdict["id"])).ToList(); Dictionary<string, int> CaptionDictID = new Dictionary<string, int>(); foreach (Dictionary<string, string> item in data) { string captionLabel = item["custom-type"]; int seqID = int.Parse(item["custom-id"]); string id = item["id"]; string name = item["name"]; string mk = item["mk"]; if (CaptionDictID.Keys.Contains(captionLabel)) { CaptionDictID[captionLabel] += 1; } else { CaptionDictID[captionLabel] = 1; } if ((new string[] { "式", "equation", "eq", "eq.", "公式" }).Contains(captionLabel.ToLower())) { mk = Regex.Replace(mk, "^\\$\\$", ""); mk = Regex.Replace(mk, "\\$\\$$", ""); mk = Regex.Replace(mk.Trim(), @"\\tag ?\{ ?(\d+-)?\d+ ?\}", ""); name = captionLabel + " " + CaptionDictID[captionLabel].ToString(); siyuanAPI.UpdateBlock(id, string.Format("$${0}\\tag{{{1}{2}}}$$", mk.Trim(), "", CaptionDictID[captionLabel])); } else { string pattern = string.Format("^(?<header>[#\\* ]*)(?<CaptionLabel>{0} \\d{{1,3}})?(?<last>.*)$", captionLabel); GroupCollection groups = Regex.Match(mk, pattern).Groups; mk = groups["header"].ToString().Trim() + captionLabel + " " + CaptionDictID[captionLabel].ToString() + " " + groups["last"].ToString().Trim(); siyuanAPI.UpdateBlock(id, mk); } JObject myJobj = new JObject(); myJobj["custom-type"] = captionLabel; myJobj["custom-id"] = CaptionDictID[captionLabel].ToString(); myJobj["name"] = captionLabel + " " + CaptionDictID[captionLabel].ToString(); siyuanAPI.SetBlockAttrs(id, myJobj); } siyuanAPI.PushMsg("已完成更新!"); } public class SYAPI { private string _TokenID; public SYAPI(string TokenID) { _TokenID = TokenID; } private async Task<JObject> CommandAsync(string path, JObject data) { string url = "http://127.0.0.1:6806"; url = url + path; var body = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"); using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Token " + _TokenID); HttpResponseMessage response = await client.PostAsync(url, body); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); return (JObject.Parse(responseBody)); } } public JObject lsNotebooksAsync() { return (CommandAsync("/api/notebook/lsNotebooks", new JObject()).Result); } public JObject PushMsg(string msg) { return CommandAsync("/api/notification/pushMsg", JObject.FromObject(new { msg = msg, timeout = 7000 })).Result; } public JObject PushErrMsg(string msg) { return CommandAsync("/api/notification/pushMsg", JObject.FromObject(new { msg = msg, timeout = 7000 })).Result; } public JObject SQL_query(string SQL) { return CommandAsync("/api/query/sql", JObject.FromObject(new { stmt = SQL, })).Result; } #region Block块 public JObject UpdateBlock(string block_id, string markdown, string dataType = "markdown") { return CommandAsync("/api/block/updateBlock", JObject.FromObject(new { id = block_id, dataType = dataType, data = markdown })).Result; } public JObject GetBlockInfo(string block_id) { return CommandAsync("/api/block/getBlockInfo", JObject.FromObject(new { id = block_id, })).Result; } public JObject GetBlockKramdown(string block_id) { return CommandAsync("/api/block/getBlockKramdown", JObject.FromObject(new { id = block_id, })).Result; } public JObject GetChildBlocks(string block_id) { return CommandAsync("/api/block/getChildBlocks", JObject.FromObject(new { id = block_id })).Result; } public JObject SetBlockAttrs(string block_id, object dict) { return CommandAsync("/api/attr/setBlockAttrs", JObject.FromObject(new { id = block_id, attrs = JObject.FromObject(dict), })).Result; } public JObject ResetBlockAttrs(string block_id, object dict) { return CommandAsync("/api/attr/resetBlockAttrs", JObject.FromObject(new { id = block_id, attrs = JObject.FromObject(dict), })).Result; } #endregion #region Document public string GetDocumentIDbyBlockID(string block_id) { JObject result = SQL_query(string.Format(" select root_id from blocks where id='{0}'", block_id)); return (string)result["data"][0]["root_id"]; } #endregion }


  1. 图 1 界面

  • 公式
    5 引用 • 24 回帖
  • 思源笔记

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

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

    26400 引用 • 109791 回帖 • 2 关注
  • C#
    29 引用 • 34 回帖 • 5 关注
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    37 引用 • 157 回帖 • 2 关注
1 操作
ttChen 在 2023-07-08 20:51:27 更新了该帖

相关帖子

欢迎来到这里!

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

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

    大佬···待会下来看看

  • ttChen

    @88250 最近在开发一款 Siyuan 的文献管理插件。已知 Siyuan 可以通过 setBlockAttrs API 函数修改块的属性,通过·updateBlock API 函数更新块的内容,都是基于块的,请问有没有一种 API 函数能更新行内元素的内容和属性?这对开发文献管理插件非常有帮助。

    image.png

    1 回复
  • 只能通过更新块 API 做到更新行内元素。

    2 回复
  • ttChen

    但通过 getBlockKramdown 或者同 SQL 获得 Markdown,如 由((20230712084706-2z48m6n \'表 1\'))\u200b可知,***{: style="background-color: var(--b3-font-background2);"},通过 updateBlock 更新后样式都消失了,即使参数加上 {: style="background-color: var(--b3-font-background2);"} 也不起作用。

    1 回复
  • updateBlockdataType 参数改成 markdown 了吗?

    1 回复
  • ttChen

    也就是说,很难保证块样式(若批注、加底色等)不变的情况,在块中增加或者删除一个简单字符。

    1 回复
  • ttChen

    改了。

  • 是的,做不到仅修改部分行级元素,用 kramdown 只能整块更新。

    1 回复
  • ttChen

    image.png

    image.png

    那有没有可能通过 API,实现由上图变成下图的效果?且底色强调不消失?

    1 回复
  • 只能把加背景的这几个字改成加粗,比如:

    { "id": "20230712190919-obqieuq", "dataType": "markdown", "data": "foo ((20230712211410-ktc4oa8 'bar')) baz **bazz**{: style=\"background-color: var(--b3-font-background1);\"}^[1]^\n{: updated=\"20230712211420\" id=\"20230712190919-obqieuq\"}" }
    1 回复
  • ttChen 1

    @88250 用网址传递参数,实现了 Zotero 文献管理,没用行内元素更新技术,不过这样也为后续导出 word 文件文献关联造成了一定的麻烦。

    实现 Siyuan 笔记的文献管理 - 链滴 (ld246.com)

  • GloR

    太强了!感谢!

请输入回帖内容 ...

推荐标签 标签

  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    29 引用 • 66 回帖 • 1 关注
  • 运维

    互联网运维工作,以服务为中心,以稳定、安全、高效为三个基本点,确保公司的互联网业务能够 7×24 小时为用户提供高质量的服务。

    151 引用 • 257 回帖
  • InfluxDB

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

    2 引用 • 105 关注
  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    497 引用 • 934 回帖
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    71 引用 • 535 回帖 • 830 关注
  • TensorFlow

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

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

    BND(Baidu Netdisk Downloader)是一款图形界面的百度网盘不限速下载器,支持 Windows、Linux 和 Mac,详细介绍请看这里

    107 引用 • 1281 回帖 • 38 关注
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    211 引用 • 358 回帖
  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    591 引用 • 3528 回帖 • 1 关注
  • RemNote
    2 引用 • 16 回帖 • 25 关注
  • Notion

    Notion - The all-in-one workspace for your notes, tasks, wikis, and databases.

    10 引用 • 77 回帖
  • 一些有用的避坑指南。

    69 引用 • 93 回帖
  • HHKB

    HHKB 是富士通的 Happy Hacking 系列电容键盘。电容键盘即无接点静电电容式键盘(Capacitive Keyboard)。

    5 引用 • 74 回帖 • 521 关注
  • Unity

    Unity 是由 Unity Technologies 开发的一个让开发者可以轻松创建诸如 2D、3D 多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

    25 引用 • 7 回帖 • 120 关注
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    172 引用 • 1541 回帖
  • JWT

    JWT(JSON Web Token)是一种用于双方之间传递信息的简洁的、安全的表述性声明规范。JWT 作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以 JSON 的形式安全的传递信息。

    20 引用 • 15 回帖 • 27 关注
  • CodeMirror
    2 引用 • 17 回帖 • 175 关注
  • 资讯

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

    56 引用 • 85 回帖
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    76 引用 • 1742 回帖 • 1 关注
  • Firefox

    Mozilla Firefox 中文俗称“火狐”(正式缩写为 Fx 或 fx,非正式缩写为 FF),是一个开源的网页浏览器,使用 Gecko 排版引擎,支持多种操作系统,如 Windows、OSX 及 Linux 等。

    7 引用 • 30 回帖 • 372 关注
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    694 引用 • 537 回帖 • 5 关注
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    183 引用 • 3885 回帖
  • Sublime

    Sublime Text 是一款可以用来写代码、写文章的文本编辑器。支持代码高亮、自动完成,还支持通过插件进行扩展。

    10 引用 • 5 回帖
  • 小薇

    小薇是一个用 Java 写的 QQ 聊天机器人 Web 服务,可以用于社群互动。

    由于 Smart QQ 从 2019 年 1 月 1 日起停止服务,所以该项目也已经停止维护了!

    35 引用 • 468 回帖 • 762 关注
  • ActiveMQ

    ActiveMQ 是 Apache 旗下的一款开源消息总线系统,它完整实现了 JMS 规范,是一个企业级的消息中间件。

    19 引用 • 13 回帖 • 685 关注
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    36 引用 • 200 回帖 • 39 关注
  • 大数据

    大数据(big data)是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。

    89 引用 • 113 回帖 • 1 关注