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

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

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

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

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

    22376 引用 • 89563 回帖
  • C#
    29 引用 • 34 回帖 • 5 关注
  • Quicker

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

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

相关帖子

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...
  • 是的,做不到仅修改部分行级元素,用 kramdown 只能整块更新。

    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)

  • updateBlockdataType 参数改成 markdown 了吗?

    1 回复
  • 查看全部回帖

推荐标签 标签

  • 阿里巴巴

    阿里巴巴网络技术有限公司(简称:阿里巴巴集团)是以曾担任英语教师的马云为首的 18 人,于 1999 年在中国杭州创立,他们相信互联网能够创造公平的竞争环境,让小企业通过创新与科技扩展业务,并在参与国内或全球市场竞争时处于更有利的位置。

    43 引用 • 221 回帖 • 115 关注
  • 996
    13 引用 • 200 回帖 • 6 关注
  • SVN

    SVN 是 Subversion 的简称,是一个开放源代码的版本控制系统,相较于 RCS、CVS,它采用了分支管理系统,它的设计目标就是取代 CVS。

    29 引用 • 98 回帖 • 682 关注
  • 深度学习

    深度学习(Deep Learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。

    53 引用 • 40 回帖
  • Bug

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

    75 引用 • 1737 回帖 • 2 关注
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    12 引用 • 54 回帖 • 62 关注
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 682 关注
  • jQuery

    jQuery 是一套跨浏览器的 JavaScript 库,强化 HTML 与 JavaScript 之间的操作。由 John Resig 在 2006 年 1 月的 BarCamp NYC 上释出第一个版本。全球约有 28% 的网站使用 jQuery,是非常受欢迎的 JavaScript 库。

    63 引用 • 134 回帖 • 724 关注
  • 分享

    有什么新发现就分享给大家吧!

    248 引用 • 1794 回帖
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 762 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    36 引用 • 37 回帖 • 530 关注
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 510 关注
  • 负能量

    上帝为你关上了一扇门,然后就去睡觉了....努力不一定能成功,但不努力一定很轻松 (° ー °〃)

    88 引用 • 1235 回帖 • 411 关注
  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    944 引用 • 943 回帖
  • 倾城之链
    23 引用 • 66 回帖 • 137 关注
  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    192 引用 • 291 回帖 • 385 关注
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    5 引用 • 26 回帖 • 530 关注
  • Webswing

    Webswing 是一个能将任何 Swing 应用通过纯 HTML5 运行在浏览器中的 Web 服务器,详细介绍请看 将 Java Swing 应用变成 Web 应用

    1 引用 • 15 回帖 • 628 关注
  • SQLServer

    SQL Server 是由 [微软] 开发和推广的关系数据库管理系统(DBMS),它最初是由 微软、Sybase 和 Ashton-Tate 三家公司共同开发的,并于 1988 年推出了第一个 OS/2 版本。

    21 引用 • 31 回帖
  • InfluxDB

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

    2 引用 • 73 关注
  • 书籍

    宋真宗赵恒曾经说过:“书中自有黄金屋,书中自有颜如玉。”

    77 引用 • 390 回帖
  • 生活

    生活是指人类生存过程中的各项活动的总和,范畴较广,一般指为幸福的意义而存在。生活实际上是对人生的一种诠释。生活包括人类在社会中与自己息息相关的日常活动和心理影射。

    230 引用 • 1454 回帖
  • WiFiDog

    WiFiDog 是一套开源的无线热点认证管理工具,主要功能包括:位置相关的内容递送;用户认证和授权;集中式网络监控。

    1 引用 • 7 回帖 • 589 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 210 关注
  • Bootstrap

    Bootstrap 是 Twitter 推出的一个用于前端开发的开源工具包。它由 Twitter 的设计师 Mark Otto 和 Jacob Thornton 合作开发,是一个 CSS / HTML 框架。

    18 引用 • 33 回帖 • 659 关注
  • GitLab

    GitLab 是利用 Ruby 一个开源的版本管理系统,实现一个自托管的 Git 项目仓库,可通过 Web 界面操作公开或私有项目。

    46 引用 • 72 回帖 • 2 关注
  • Mac

    Mac 是苹果公司自 1984 年起以“Macintosh”开始开发的个人消费型计算机,如:iMac、Mac mini、Macbook Air、Macbook Pro、Macbook、Mac Pro 等计算机。

    166 引用 • 595 回帖 • 1 关注