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

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

对图、表和公式的自动编号和交叉引用是科研笔记最基本的功能,但当前各种笔记系统均未实现该功能。因此,抽出一天时间,利用 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 回帖
  • 思源笔记

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

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

    25449 引用 • 105260 回帖
  • C#
    29 引用 • 34 回帖 • 5 关注
  • Quicker

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

    37 引用 • 157 回帖
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

    太强了!感谢!

请输入回帖内容 ...

推荐标签 标签

  • Word
    13 引用 • 41 回帖 • 1 关注
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    730 引用 • 1280 回帖 • 1 关注
  • gRpc
    11 引用 • 9 回帖 • 89 关注
  • 链书

    链书(Chainbook)是 B3log 开源社区提供的区块链纸质书交易平台,通过 B3T 实现共享激励与价值链。可将你的闲置书籍上架到链书,我们共同构建这个全新的交易平台,让闲置书籍继续发挥它的价值。

    链书社

    链书目前已经下线,也许以后还有计划重制上线。

    14 引用 • 257 回帖
  • CentOS

    CentOS(Community Enterprise Operating System)是 Linux 发行版之一,它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定的服务器以 CentOS 替代商业版的 Red Hat Enterprise Linux 使用。两者的不同在于 CentOS 并不包含封闭源代码软件。

    239 引用 • 224 回帖
  • RemNote
    2 引用 • 16 回帖 • 14 关注
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖
  • 互联网

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

    99 引用 • 367 回帖 • 1 关注
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 384 回帖 • 10 关注
  • abitmean

    有点意思就行了

    34 关注
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    21 引用 • 245 回帖 • 226 关注
  • Anytype
    3 引用 • 31 回帖 • 16 关注
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    412 引用 • 3588 回帖
  • CSDN

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

    14 引用 • 155 回帖 • 1 关注
  • HTML

    HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。

    108 引用 • 295 回帖
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    181 引用 • 408 回帖 • 483 关注
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 489 关注
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 74 关注
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 666 关注
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    2 引用 • 14 回帖 • 4 关注
  • Oracle

    Oracle(甲骨文)公司,全称甲骨文股份有限公司(甲骨文软件系统有限公司),是全球最大的企业级软件公司,总部位于美国加利福尼亚州的红木滩。1989 年正式进入中国市场。2013 年,甲骨文已超越 IBM,成为继 Microsoft 后全球第二大软件公司。

    107 引用 • 127 回帖 • 336 关注
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    89 引用 • 150 回帖
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 2 关注
  • ActiveMQ

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

    19 引用 • 13 回帖 • 680 关注
  • 人工智能

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

    167 引用 • 314 回帖
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖