笔记下面反链框里的反链笔记--能显示其下面的反链数量吗?

「a笔记」下面有反链「b笔记」
「b笔记」下面有反链「b笔记1」和「b笔记2」
那么打开「a笔记」,反链框里的「b笔记」后面能显示「b笔记」下的反链数量“2”吗
【这个反链数量“2”是:「b笔记1」和「b笔记2」】

不知道是不是这个意思: 要给官方核心插件的面板 (这里特指反链面板) 里, 增加某些需要显示的属性

其实如果退一步, 不要求非得在核心插件里实现, 那这还挺容易的, 比如

显示当前笔记的反链笔记被引用状态

```dataview
table length(file.inlinks) as 引用数, string(file.inlinks) as 列表
from [[]]
```

这就行了…



以下说的是, 如果就要在 “反链面板” 增加些信息, 怎么整

目前预备分解为几个部分来做

  • A 弄到当前笔记的反链笔记的数据
  • B 找到反链面板的 dom 对象 (更准确说是反链面板里, “笔记标题” 那个元素)
  • C 把以上标题数据给改了, 争取做到重复调用结果一致
  • D 把整件事自动化

A 找到当前笔记的反链笔记

如下, 可以在 console 里实验:

var active_file = app.workspace.getActiveFile();
var dv = app.plugins.plugins.dataview.api;
dv.page(active_file.path).file.inlinks.map(link => console.log(
	link.path, 
	dv.page(link).file.name, 
	dv.page(link).file.inlinks.length)
)

B 找到反链面板的标题

如下, 可以在 console 里实验, 要求开着反链面板:

document.querySelectorAll('.backlink-pane .search-result-file-title .tree-item-inner').forEach(elem => {
	console.log(elem.innerText); 
})

C 把标题数据给改了

综合前两段, 且最好是反复执行后使结果不变

await sleep(2000);
// new Notice('将要执行反链面板增补属性');
var dv = app.plugins.plugins.dataview.api;
var backlink_data = dv.page(app.workspace.getActiveFile().path).file.inlinks.array().reduce((acc, link) => {
	acc[dv.page(link).file.name] = 
	dv.page(link).file.inlinks.length;   // <-- 收集 "反链数" 可以改为其他属性
	return acc;
}, {});
console.log(backlink_data);

document.querySelectorAll('.backlink-pane .search-result-file-title .tree-item-inner').forEach(elem => {
	const note_title = elem.innerText.split(' [Cite')[0];
	elem.innerText = note_title + ` [Cite ${backlink_data[note_title]}]`;
});


该代码执行后如下图, 黄色高亮文字 Cite N 是脚本添进去的, 表示该笔记的反链数, 这也可替换为自己感兴趣的其他属性

D 自动化

运行 C 会发现基本能达到目的
自动化也许应该考虑 QuickAdd 等方案


仍有以下问题没考虑好:

  • 如何 以 API 方式, 而非编辑 dom 的方式, 修改核心插件的行为, 例如改反链面板内的按钮, 显示文字等? 我目前觉得没办法做到
  • 如何拿到反链面板的每条笔记的全路径? 在 dom 元素里我没找到笔记 hash, id, 全路径之类信息
    • 也因此目前有个 bug: 如果该笔记有多个重名的反链笔记, 则从反链面板的标题无法区分出它们

PS. 论坛里有许多擅长写脚本的伙伴, 以及大量教程资源 (我快学不过来了…), 感觉肯定有比我这更好的方案, 希望大家给出出主意

恩,是的,能实现「c 把标题数据给改了」的步骤就达到了想要的了
可是要怎么才能实现呢? 不懂! :sweat_smile: :rofl:

最懒的办法是, 按 Ctrl+Shift+i 出控制台, 把 “C” 里的代码粘进去, 回车, 看看是否符合需要

如果使用频次不高, 建议就这么用几回, 就完了, 别折腾了

如果感到需要长久使用, 咱们可以研究怎么做到更加自动化, 我自己对 QuickAdd Templater 等都不是特别熟, 但这些肯定是对自动执行有帮助的, 最近还刚知道一个 RunJS 看着也不错

恩,我是想常用的,
我的笔记是主题式的,反链相当于归类「子类」。
我想尽可能在本主题下的看到更多的相关「反链」的反链「子类」

那把 “C” 的代码抄到 任意名字.js 文件里, 搁到 RunJS 插件里当个脚本用吧
这插件配置起来比较省心

PS. 我感觉你这需求 (关注反链的反链) 也可以考虑 zsviczian/excalibrainSkepticMystic/breadcrumbs

我把把 “C” 的代码抄到 任意名字.js 文件里,然后放到根目录
刚开始还能运行,
重新打开载入软件后,就这样了,没有办法用了

它是这么个意思: RunJS 有四个可添加脚本的栏目, Auto start, Command, Ribbon icon, Event handler, 可以看出, 其实都是在执行脚本, 只是啥时去执行? 时机不同

  • Auto start, 它介绍里写着, 当插件加载后执行
  • Command, 注册为 Obsidian 命令, 按照 Ob 命令行那一套来管
  • Ribbon icon, 显示为界面最左侧窄栏按钮 (Ribbon 工具条) 用户自己点击执行
  • Event handler, 在指定的事件发生时执行, "事件"就是比如打开笔记, 改变窗口大小, 等等

然后再说这个脚本的机制: 找到当前笔记的反链笔记, 把属性给补充到反链面板里


所以, 这不能只在插件加载后执行一次, 而是得在每次笔记变了都运行一次

我是搁到如图两处位置了

此外, 我又加了个 await sleep(2000); 让这脚本稍缓一会儿再执行, 这句已经补充到之前的 “C” 代码里

(也许还有更合理的触发执行时机, 希望熟悉的朋友给指点完善)

1 个赞

好的,非常感谢 :pray:
这个cite能去掉去吗?或者改成这个“:link:”图标
image

能看出来楼主不太敢动别人代码
其实挺简单的, 就是这两句:

...
	const note_title = elem.innerText.split(' [Cite')[0];
	elem.innerText = note_title + ` [Cite ${backlink_data[note_title]}]`;
...

给改成

...
	const note_title = elem.innerText.split(' [🔗')[0];
	elem.innerText = note_title + ` [🔗 ${backlink_data[note_title]}]`;
...

关键词: 字符串插值, 字符串分割

这种小细节建议楼主自己动手试试, 弄错了大不了回退
另外, 这也是为了解别人代码干了啥? 你心里能有个底

好的好的,谢谢指教!
刚才是自己替换了一下的,不知道怎么的,反链里出现很多undedined!
现在好了,再次感谢! :pray:

file-open有个问题,我的理解是它只在文件首次被打开时执行,之后再次打开该文件就不执行了,比如,打开a.md文件后,按Ctrl+点击新标签再次打开一个a.md文件,则不会执行file-open事件了,我通常用active-leaf-change配合setTimeout来模拟,但由于active-leaf-change在切换标签或打开文件是可能被触发多次,所以要用防抖机制保证最后一次执行的是目标文件,可参考:topic/25760#35 。但如果使用active-leaf-change,就会在标签切换时也被执行,所以为了不产生副作用,代码里要保证操作的幂等性(所谓幂等性就是指多次操作结果是一致的,不产生意外情况)。

1 个赞

感谢, 试了下确实这个 active-leaf-change 办法更好~
这个防抖写法也可以用在许多脚本里


to 楼主:
我现在改成了下面这样, 放在 RunJS 的 Auto start 里
这写法触发 “补充反链面板属性” 的时机更合理些

// 参考 https://forum-zh.obsidian.md/t/topic/25760/35
this.app.workspace.onLayoutReady(() => {
    let stTimer;
    // new Notice(`将要执行反链面板增补属性 stTimer ${stTimer}`);
	this.app.workspace.on('active-leaf-change', async (activeLeaf) => {
		// 定时防止无效触发,只取最后一个触发
		if(stTimer) clearTimeout(stTimer);
		// new Notice(`重新设置 stTimer ${stTimer}`);
		stTimer = setTimeout(() => {
            patch_backlink_pane();
        }, 2000)
    })
})

function patch_backlink_pane() {
	var dv = app.plugins.plugins.dataview.api;
	var backlink_data = dv.page(app.workspace.getActiveFile().path).file.inlinks.array().reduce((acc, link) => {
		acc[dv.page(link).file.name] = 
		dv.page(link).file.inlinks.length;   // <-- 收集 "反链数" 可以改为其他属性
		return acc;
	}, {});
	console.log(backlink_data);

	document.querySelectorAll('.backlink-pane .search-result-file-title .tree-item-inner').forEach(elem => {
		const note_title = elem.innerText.split(' [Cite')[0];
		elem.innerText = note_title + ` [Cite ${backlink_data[note_title] ?? '-'}]`;
	});
}
1 个赞

嗯嗯嗯,非常感谢 :pray: