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

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

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

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

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

    22370 引用 • 89531 回帖
  • C#
    29 引用 • 34 回帖 • 5 关注
  • Quicker

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

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

相关帖子

欢迎来到这里!

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

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

    image.png

    image.png

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

    1 回复
  • 其他回帖
  • GloR

    太强了!感谢!

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

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

    {
        "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 回复
  • 查看全部回帖

推荐标签 标签

  • 服务

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

    41 引用 • 24 回帖 • 1 关注
  • GitHub

    GitHub 于 2008 年上线,目前,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。正因为这些功能所提供的便利,又经过长期的积累,GitHub 的用户活跃度很高,在开源世界里享有深远的声望,并形成了社交化编程文化(Social Coding)。

    209 引用 • 2031 回帖
  • abitmean

    有点意思就行了

    30 关注
  • ActiveMQ

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

    19 引用 • 13 回帖 • 673 关注
  • PostgreSQL

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

    22 引用 • 22 回帖
  • Docker

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

    491 引用 • 917 回帖 • 2 关注
  • 分享

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

    248 引用 • 1794 回帖
  • H2

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

    11 引用 • 54 回帖 • 653 关注
  • V2Ray
    1 引用 • 15 回帖 • 2 关注
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 462 关注
  • 微信

    腾讯公司 2011 年 1 月 21 日推出的一款手机通讯软件。用户可以通过摇一摇、搜索号码、扫描二维码等添加好友和关注公众平台,同时可以将自己看到的精彩内容分享到微信朋友圈。

    130 引用 • 793 回帖
  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖
  • Vditor

    Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。它使用 TypeScript 实现,支持原生 JavaScript、Vue、React 和 Angular。

    352 引用 • 1815 回帖
  • Hprose

    Hprose 是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。

    9 引用 • 17 回帖 • 609 关注
  • BND

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

    107 引用 • 1281 回帖 • 27 关注
  • Eclipse

    Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。

    75 引用 • 258 回帖 • 618 关注
  • 思源笔记

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

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

    22370 引用 • 89527 回帖
  • Kotlin

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

    19 引用 • 33 回帖 • 66 关注
  • Bug

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

    75 引用 • 1737 回帖 • 2 关注
  • 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.

    6 引用 • 63 回帖
  • 房星科技

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

    6 引用 • 141 回帖 • 585 关注
  • 互联网

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

    98 引用 • 344 回帖
  • 安装

    你若安好,便是晴天。

    132 引用 • 1184 回帖
  • CSS

    CSS(Cascading Style Sheet)“层叠样式表”是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。

    198 引用 • 550 回帖 • 2 关注
  • WordPress

    WordPress 是一个使用 PHP 语言开发的博客平台,用户可以在支持 PHP 和 MySQL 数据库的服务器上架设自己的博客。也可以把 WordPress 当作一个内容管理系统(CMS)来使用。WordPress 是一个免费的开源项目,在 GNU 通用公共许可证(GPLv2)下授权发布。

    66 引用 • 114 回帖 • 228 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    25 引用 • 191 回帖 • 15 关注
  • 机器学习

    机器学习(Machine Learning)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。

    83 引用 • 37 回帖 • 1 关注