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

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

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

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

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

    24531 引用 • 100414 回帖
  • C#
    29 引用 • 34 回帖 • 5 关注
  • Quicker

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

    36 引用 • 155 回帖
1 操作
ttChen 在 2023-07-08 20:51:27 更新了该帖

相关帖子

欢迎来到这里!

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

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

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

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

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

    { "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 回复
  • GloR

    太强了!感谢!

  • ttChen

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

    1 回复
  • 查看全部回帖

推荐标签 标签

  • 互联网

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

    99 引用 • 361 回帖
  • 30Seconds

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

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

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

    584 引用 • 3537 回帖
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    31 引用 • 108 回帖
  • 周末

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

    14 引用 • 297 回帖
  • SendCloud

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

    2 引用 • 8 回帖 • 488 关注
  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖
  • 代码片段

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

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

    126 引用 • 848 回帖
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 67 回帖 • 442 关注
  • FlowUs

    FlowUs.息流 个人及团队的新一代生产力工具。

    让复杂的信息管理更轻松、自由、充满创意。

    1 引用 • 2 关注
  • Tomcat

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

    162 引用 • 529 回帖 • 7 关注
  • Logseq

    Logseq 是一个隐私优先、开源的知识库工具。

    Logseq is a joyful, open-source outliner that works on top of local plain-text Markdown and Org-mode files. Use it to write, organize and share your thoughts, keep your to-do list, and build your own digital garden.

    7 引用 • 69 回帖 • 4 关注
  • ngrok

    ngrok 是一个反向代理,通过在公共的端点和本地运行的 Web 服务器之间建立一个安全的通道。

    7 引用 • 63 回帖 • 650 关注
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 74 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 52 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 664 关注
  • Love2D

    Love2D 是一个开源的, 跨平台的 2D 游戏引擎。使用纯 Lua 脚本来进行游戏开发。目前支持的平台有 Windows, Mac OS X, Linux, Android 和 iOS。

    14 引用 • 53 回帖 • 545 关注
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    22 引用 • 22 回帖 • 2 关注
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖 • 1 关注
  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 497 关注
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 588 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖 • 1 关注
  • Flutter

    Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

    39 引用 • 92 回帖 • 1 关注
  • Laravel

    Laravel 是一套简洁、优雅的 PHP Web 开发框架。它采用 MVC 设计,是一款崇尚开发效率的全栈框架。

    20 引用 • 23 回帖 • 737 关注
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用 • 1 关注
  • Sillot

    Insights(注意当前设置 master 为默认分支)

    汐洛彖夲肜矩阵(Sillot T☳Converbenk Matrix),致力于服务智慧新彖乄,具有彖乄驱动、极致优雅、开发者友好的特点。其中汐洛绞架(Sillot-Gibbet)基于自思源笔记(siyuan-note),前身是思源笔记汐洛版(更早是思源笔记汐洛分支),是智慧新录乄终端(多端融合,移动端优先)。

    主仓库地址:Hi-Windom/Sillot

    文档地址:sillot.db.sc.cn

    注意事项:

    1. ⚠️ 汐洛仍在早期开发阶段,尚不稳定
    2. ⚠️ 汐洛并非面向普通用户设计,使用前请了解风险
    3. ⚠️ 汐洛绞架基于思源笔记,开发者尽最大努力与思源笔记保持兼容,但无法实现 100% 兼容
    29 引用 • 25 回帖 • 108 关注