obsidian常用api汇总

获取和修改文件的配置

this.app.vault.getConfig("option")
// 这里的option怎么获取
// 1 用devtools查看选项字段,在app.js中搜索
// 2 打开devtools,修改设置选项,然后用下面的代码查看具体选项
// this.app.vault.config
// 修改文件配置
app.vault.setConfig("vimMode", false)

通过命令 id 执行命令

this.app.commands.executeCommandById('app:open-settings');
//查看命令列表
// app.commands.listCommands().filter(f=>[f.id, f.name].some(i=>i.contains('runjs')))
//app.commands.listCommands().filter(f=>f.id.contains('runjs'))
//app.commands.listCommands().filter(f=>f.name.contains('runjs'))

command 命令的动态添加、修改、删除和查找

// 添加命令
app.commands.addCommand({
    "id": "runjs:command-test3",
    "name": "RunJS: runjs test3",
    mobileOnly: false, //是否仅手机显示
    showOnMobileToolbar: false, //是否在手机工具栏显示
    icon: "lucide-bold", //图标
    //快捷键 see https://www.electronjs.org/zh/docs/latest/api/accelerator
    hotkeys: [{ modifiers: ["Mod", "Shift"], key: "N" }],
    //hotkeys: [{ modifiers: ["Mod", "Alt"], key: "ArrowLeft" }]
    repeatable: false, //是否可重复执行
    allowPreview: false, //是否允许预览模式执行
    allowProperties: true, //是否允许文档属性中执行
    callback: () => {console.log('333');alert(333);}
})

// 修改命令
app.commands.commands['runjs:command-2'].name = 'RunJS: runjs test2';
//app.commands.commands['runjs:command-2']和app.commands.findCommand('runjs:command-2')等价
app.commands.findCommand('runjs:command-2').callback = ()=>{console.log('111');alert(111);}
app.commands.commands['xxxx'].checkCallback = (checking)=>{}
app.commands.commands['xxxx'].editorCallback = (editor, view)=>{}
app.commands.commands['xxxx'].editorCheckCallback = (checking, editor, view)=>{}

// 删除命令
app.commands.removeCommand('runjs:command-2');

// 查找命令
app.commands.findCommand('runjs:command-2')
app.commands.listCommands().filter(f=>[f.id, f.name].some(i=>i.contains('runjs')))

// 执行命令
app.commands.executeCommandById('app:open-settings');

快捷键管理

// 添加快捷键
// see https://www.electronjs.org/zh/docs/latest/api/accelerator
app.hotkeyManager.addDefaultHotkeys(commandId, [{ modifiers: ["Mod", "Alt"], key: "ArrowLeft" }])

//获取默认快捷键
app.hotkeyManager.getDefaultHotkeys(commandId)

// 删除快捷键
app.hotkeyManager.removeDefaultHotkeys(commandId);

// 获取快捷键
app.hotkeyManager.getHotkeys(commandId);

// 修改快捷键
app.hotkeyManager.setHotkeys(commandId)

// 打印快捷键
app.hotkeyManager.printHotkeyForCommand(commandId)

// 重新加载快捷键
app.hotkeyManager.load()

// 保存快捷键
app.hotkeyManager.save();

添加,修改,删除 frontmatter

const file = this.app.vault.getFileByPath("demo.md");
if(file) {
	//see https://forum.obsidian.md/t/how-to-use-app-filemanager-processfrontmatter/73467
	this.app.fileManager.processFrontMatter(file, (frontmatter: any) => {
		frontmatter['a'] = "aaaa";
		frontmatter['b'] = "bbbb";
		frontmatter['c'] = "cccc";
	})
}

//清空当前文件frontmatter
// 如果当前文件可用 app.workspace.getActiveFile()
const file = app.vault.getFileByPath("你的文件名.md");
app.fileManager.processFrontMatter(file, (frontmatter) => { for(i in frontmatter){delete frontmatter[i]} });

//清空所有文件frontmatter
// 郑重声明:如果你不知道这段代码在干什么,请不要测试!!!
// 危险!危险!危险!请在沙盒中测试无误后使用,后果自负!!!
app.vault.getFiles().forEach(p=>{
    // 只对markdown文件操作
    if(p.extension==="md"){
        const file = app.vault.getFileByPath(p.path);
        app.fileManager.processFrontMatter(file, (frontmatter) => { for(i in frontmatter){delete frontmatter[i]} });
    }
});

读取 frontmatter

// 从缓存读取
let fm = this.app.metadataCache.getFileCache(file)?.frontmatter
console.log(fm)

// 从文件解析
import { getFrontMatterInfo, parseYaml } from 'obsidian';
const content = await this.app.vault.read(file)
const yaml = getFrontMatterInfo(content).frontmatter
const yamlObj = parseYaml(yaml)
console.log(yamlObj)

激活叶子

this.app.workspace.setActiveLeaf(leaf, { focus: true });

复制到剪切板

navigator.clipboard.writeText('');

记录叶子的光标和滚动条位置

onst state = leaf.getEphemeralState()
setTimeout(() => {
    leaf.setEphemeralState(state);
}, 42);

关闭重复标签

this.app.workspace.onLayoutReady(() => {
	this.registerEvent(
		this.app.workspace.on("active-leaf-change", (activeLeaf) => {
			const filePath = activeLeaf?.view.getState().file;
			if (!filePath) return;
			const viewType = activeLeaf?.view.getViewType();
			// 获取已存在的其他叶子
			let repeatLeaves = this.app.workspace.getLeavesOfType(viewType).filter((leaf) =>
				// 不包含新打开的叶子
				leaf !== activeLeaf &&
				// 限制同一个分割窗口下的叶子
				//leaf.parent.id === activeLeaf.parent.id &&
				// 筛选文件路径相同的叶子
				leaf.view?.getState().file === filePath
			)
			if(repeatLeaves.length > 0) {
				repeatLeaves.push(activeLeaf)
				// 排序规则,pinned优先,相同条件下,最先打开的优先
				repeatLeaves = repeatLeaves.sort((leaf1, leaf2) => {
					// 如果 leaf1 被固定但 leaf2 没有,leaf1 在前
					if (leaf1.pinned && !leaf2.pinned) return -1;
					// 如果 leaf2 被固定但 leaf1 没有,leaf2 在前
					if (!leaf1.pinned && leaf2.pinned) return 1;
					// 当两者都 pinned 或者都没有 pinned 的情况,根据 activeTime 排序
					const leaf2ActiveTime =leaf2.activeTime || Date.now()
					if (leaf1.activeTime !== leaf2ActiveTime) {
						// 时间早的在前
						return leaf1.activeTime - leaf2ActiveTime;
					}
					// activeTime 相同的情况下,保持原顺序
					return 0;
				});

				// 激活排序后的第一个叶子
				const newActiveLeaf = repeatLeaves.shift();
				this.app.workspace.setActiveLeaf(newActiveLeaf, { focus: true });

				// 关闭排序后的其他叶子
				repeatLeaves.forEach((leaf) => {
					leaf?.detach()
				});
			}
			// 如果设置为默认锁定体验更佳,但锁定的叶子关闭需要点击两次或者右键关闭
			// 这样相当于每次都新标签打开
			// setTimeout(() => {
			// 	const currActiveLeaf = this.app.workspace.getActiveViewOfType(ItemView)?.leaf;
			// 	if(!currActiveLeaf?.pinned) currActiveLeaf?.setPinned(true);
			// }, 42);
		})
	)
})

用默认应用打开文件(也可以打开文件夹)

this.app.openWithDefaultApp(FolderOrFile)

获取 .obsidian 目录

const vault = this.app.vault
const adapter = vault.adapter
const obConfigDir = adapter.path.join(adapter.basePath, vault.configDir)
console.log(obConfigDir)

获取当前激活 view 的类型

this.app.workspace.activeLeaf.view.getViewType()

获取 rootSplit

@ts-ignore
this.app.workspace.rootSplit.containerEl

当未开启番茄工作法且鼠标不在状态栏焦点时自动隐藏状态栏

/* 当未开启番茄工作法且鼠标不在状态栏焦点时隐藏状态栏 */
.status-bar:not(:hover):has(.plugin-obsidian-statusbar-pomo:empty) {
    opacity: 0;
}

notice 倒计时

let count = 10
const a=new Notice("hello "+count, 0);
while (count > 0) {
count--;
await new Promise((resolve) => setTimeout(resolve, 1000)).then(() => {a.setMessage("hello "+count);});
}
a.hide()

在当前叶子打开文件

await app.workspace.activeLeaf.openFile(app.vault.getAbstractFileByPath("demo.md"))

在左侧面板打开文件

app.workspace.getLeftLeaf().openFile(app.vault.getAbstractFileByPath("demo.md"))

新窗口打开文件

await app.workspace.getLeaf('tab').openFile(app.vault.getAbstractFileByPath('demo.md'))

obsidian 插件外 require(‘obsidian’)

  1. 先去github安装这个插件obsidian-fix-require-modules并开启插件。

  2. 如下方法调用

const ob = require('obsidian');
const markdown = ob.htmlToMarkdown(htmlContent);

htmlToMarkdown 在非 obsidian 环境可以用 turndown,貌似 obsidian 中就用的这个项目。

但在 runjs 中科院直接导入 obsidian,比如:

//在runjs中可以这样导入
import * as obsidian from 'obsidian';
obsidian.setIcon(document.querySelector(".side-dock-settings > .side-dock-ribbon-action:nth-child(3) > svg").parentElement, 'document');

写文档

app.vault.adapter.write("demo.md", 'demo')

在新窗口打开一个当前文档

新窗口打开,原窗口还在,打开的是副本
app.commands.executeCommandById(“workspace:open-in-new-window”);

新窗口打开,原窗口光标,替换方式打开
app.commands.executeCommandById(“workspace:move-to-new-window”);

以置顶方式打开新窗口

感谢 @熊猫别熬夜 噗~ 提供

const openFilePath = "todo.md"; // 设置打开的文件路径
const activeWindowWidth = 380; // 设置新窗口的宽度
const activeWindowHeight = 500; // 设置新窗口的高度

// 获取屏幕大小
const screenWidth = activeWindow.screen.width;
const screenHeight = activeWindow.screen.height;

// 计算新窗口的位置和大小
const activeWindowLeft = (screenWidth - activeWindowWidth) / 2;
const activeWindowTop = (screenHeight - activeWindowHeight) / 2;

//打开文档
app.workspace.getLeaf('window').openFile(app.vault.getAbstractFileByPath(openFilePath));

// 暂停5ms
await sleep(5)
//await new Promise(resolve => setTimeout(resolve, 5));

// 将激活窗口置顶
activeWindow.electronWindow.setAlwaysOnTop(true);

// 调整窗口大小为宽度,高度
activeWindow.resizeTo(activeWindowWidth, activeWindowHeight);

// 设置窗口的位置和大小
activeWindow.moveTo(activeWindowLeft, activeWindowTop);

参考:我开发了一个可以把某笔记屏幕置顶的小工具 - 经验分享 - Obsidian 中文论坛

也可以参考:Float on Top for Mac? - Basement - Obsidian Forum

let getWindow = require('electron').remote.getCurrentWindow();
getWindow.setOpacity(0.88);
getWindow.setAlwaysOnTop(true);

外部执行命令

// mac
open --background "obsidian://advanced-uri?vault=vault_name&commandid=runjs%253Acommand-0"

//linux
xdg-open "obsidian://advanced-uri?vault=vault_name&commandid=runjs%253Acommand-0"

//windows
start /b "obsidian://advanced-uri?vault=vault_name&commandid=runjs%253Acommand-0"

// 判断当前主题

// 方法1
let theme = app.getTheme();
if(theme == 'moonstone'){
    theme = 'light'
} else if(theme == 'obsidian') {
    theme = 'dark'
} else {
    const isLight = window.matchMedia("(prefers-color-scheme: light)");
    if (isLight.matches) {
        theme = 'light'
    } else {
      theme = 'dark'
    }
}

// 方法2
const theme = document.body.classList.contains("theme-light") ? 'light' : 'dark';

获取系统插件

this.app.internalPlugins.getEnabledPluginById("bookmarks")

判断插件已启用

const metaeditEnabled = app.plugins.enabledPlugins.has("metaedit");

获取已安装插件信息

app.plugins.manifests

插件管理

//判断是否安装
if (!app.plugins.plugins[pluginId]) {
	console.log(`插件 ${pluginId} 未安装`);
}
//禁用插件
await app.plugins.disablePlugin(pluginId);
// 等待一段时间以确保插件完全禁用
await new Promise(r => setTimeout(r, 1000));
console.log(`插件 ${pluginId} 已禁用`);

// 启用插件
await app.plugins.enablePlugin(pluginId);
console.log(`插件 ${pluginId} 已启用`);

// 重新加载插件
// 先禁用再启用

//卸载插件
//先禁用插件,再删除插件目录

//重置插件
//先禁用,删除插件data.json配置,再启用插件

删除文件

const trashOption = this.app.vault.getConfig("trashOption");
if(trashOption === 'system') {
	// 移动到系统回收站
	await app.vault.trash(app.vault.getAbstractFileByPath(path), true);
} else if(trashOption === 'local') {
	// 移动到obsidian的.trash目录
	await app.vault.trash(app.vault.getAbstractFileByPath(path), false);
} else {
	// 永久删除
	await app.vault.adapter.remove(path);
	// 也可以用app.vault.delete
	//await app.vault.delete(app.vault.getAbstractFileByPath(path))
}

添加,删除,修改书签

//查看
// 按分组和未分组分开展示
app.internalPlugins.plugins.bookmarks.instance.items
// 全部展示
app.internalPlugins.getEnabledPluginById("bookmarks").getBookmarks()

//添加
// type类型,file,folder,search,group
// 添加文件书签
app.internalPlugins.plugins.bookmarks.instance.addItem({
  "type": "file",
  "ctime": 1716495589884,
  "path": "未命名 2.md"
})
// 添加分组书签
app.internalPlugins.plugins.bookmarks.instance.addItem({
  "type": "group",
  "ctime": 1715068875125,
  "items": [
	{
	  "type": "file",
	  "ctime": 1715068911064,
	  "path": "linux/Linux find 模拟 tree 命令.md"
	}
  ],
  "title": "分组2"
})

// 删除
app.internalPlugins.plugins.bookmarks.instance.removeItem(app.internalPlugins.plugins.bookmarks.instance.items[0])

// 修改
// 先更新app.internalPlugins.plugins.bookmarks.instance.items的值,
// 再执行app.internalPlugins.plugins.bookmarks.instance.editItem(),比如

app.internalPlugins.plugins.bookmarks.instance.items[1]['title']='分组22'
app.internalPlugins.plugins.bookmarks.instance.editItem();

//也可以直接修改.obsidian/bookmarks.json

//打开书签面板
app.workspace.ensureSideLeaf('bookmarks', "left", { active: true})

//判断是否已添加到书签
app.internalPlugins.plugins.bookmarks.bookmarkLookup.hasOwnProperty(path)

在资源管理器中显示文件

// 在资源管理器中显示文件
app.showInFolder('demo.md');
// 在资源管理器中显示文件夹
app.showInFolder('tools');

取消注册事件

app.workspace.off("editor-menu", menuCallback);

不安装 nodejs 开发插件

// 放到main.js中
const ob = require('obsidian')
module.exports = class extends ob.Plugin {
  constructor(app, manifest) {
    super(app, manifest)
    
  }
  onload() {
    // 这里注册一个命令以展示其中一个 modal
    this.addCommand({
      id: 'test-my-demo', name: 'Test my Demo',
      callback: async ()=> {
        alert('Test my Demo')
      }
    })
  }
  onunload() {}
}

通过接口打开 custom frames 的窗口

// 删除文心一言叶子
app.workspace.detachLeavesOfType('custom-frames-文心一言')

//重新打开文心一言叶子,right显示右侧,left显示左侧,active:false仅打开不激活叶子
// ensureSideLeaf函数会检查是否已打开,如果已打开不再打开
app.workspace.ensureSideLeaf('custom-frames-文心一言', "right", { active: true})

//也可以通过custom frames提供的接口打开
// 该接口有三个参数:第一个参数同上是视图类型,第二个参数true中间视图打开,false右侧面板打开,第三个参数,'split分割窗口打开,'tab'新标签打开,'window'新窗口打开
app.plugins.plugins['obsidian-custom-frames'].openLeaf('custom-frames-文心一言, false)

注意:这里的”文心一言“是你在custom frames里输入的名字,根据自己需要修改,但必须有”custom-frames-“前缀,即”custom-frames-你填写的名字“

遍历叶子的方法

//遍历所有叶子
app.workspace.iterateAllLeaves((leaf)=>{console.log(leaf)});
//遍历指定视图类型的叶子
app.workspace.getLeavesOfType('markdown');
//遍历左侧面板的叶子
app.workspace.iterateLeaves(app.workspace.leftSplit, (leaf)=>{console.log(leaf)})
//遍历右侧面板的叶子
app.workspace.iterateLeaves(app.workspace.rightSplit, (leaf)=>{console.log(leaf)})
//遍历中间面板的叶子
app.workspace.iterateLeaves(app.workspace.rootSplit, (leaf)=>{console.log(leaf)})
app.workspace.iterateRootLeaves((leaf)=>{console.log(leaf)})
//遍历floatingSplit叶子(虽然还不知道floating分割窗口是啥)
app.workspace.iterateLeaves(app.workspace.floatingSplit, (leaf)=>{console.log(leaf)})

// 把叶子从隐藏状态显示出来(即获取焦点,但不是激活)
leaf = this.app.workspace.getLeavesOfType('bookmarks')[0];
this.app.workspace.revealLeaf(leaf);

获取叶子

源码:

const getLeaf = function(type, tabData) {
    if (type === "split") {
        return this.splitActiveLeaf(tabData);
    } else if (type === "tab" || type === true) {
        return this.createLeafInTabGroup();
    } else if (type === "window") {
        return this.openPopoutLeaf();
    } else {
        return this.getUnpinnedLeaf();
    }
}

由源码可知

app.workspace.getLeaf() // 当前标签
app.workspace.getLeaf('tab') // 新标签
app.workspace.getLeaf('split', 'vertical') // 垂直拆分
app.workspace.getLeaf('split', 'horizontal') // 水平拆分
app.workspace.getLeaf('window') // 新窗口

app.workspace.getLeftLeaf(); // 新建左侧边栏
app.workspace.getLeftLeaf(false); // 新建左边栏水平拆分
app.workspace.getLeftLeaf(); // 新建右侧边栏
app.workspace.getLeftLeaf(false); // 新建右栏水平拆分
app.workspace.getMostRecentLeaf(); // 最近的叶子
app.workspace.ensureSideLeaf(); // 显示或创建面板
app.workspace.revealLeaf(); // 显示面板

获取平台类型

window.process.platform
//或
require('obsidian').Platform

获取文档编辑器

const editor = app.workspace.getActiveFileView()?.editor
//或
//const editor = app.workspace.activeEditor?.editor
//插入文本到最后
editor.insertText("\naaaaa")
//插入文本到某行
editor.replaceRange("| 22  | 33  | 66  |\n", { line: 13, ch: 0 })

// 获取文本块行
const file = app.metadataCache.getFileCache(app.vault.getAbstractFileByPath("demo.md"))
const tables = file.sections.filter(s=>s.type === 'table')
//或
// const table = file.blocks["block-name"]

// 光标移动事件
this.app.workspace.on("editor-selection-change", (t,n,i)=>{console.log(t,n, i)})

// 获取光标处的元素
document.querySelectorAll(".workspace-split.mod-root .cm-active.cm-line")

// html,表格渲染时执行
this.registerMarkdownPostProcessor((element, context) => {});

获取激活文档的元素

activeDocument.activeElement

常用方法、函数

// 后退
app.workspace.activeLeaf.history.back()
// 前进
app.workspace.activeLeaf.history.forward()

// 切换主题
app.changeTheme("obsidian") //暗色主题
app.changeTheme("moonstone") //亮色主题

// 打开设置
app.setting.open()

// 打开更新内容
app.showReleaseNotes()

//触发预览和编辑模式
app.workspace.activeEditor.toggleMode()

//调用添加文档属性
app.workspace.activeLeaf.view.metadataEditor.addProperty()

//关闭当前标签页
app.workspace.activeLeaf.detach()

// 触发左侧边栏
app.workspace.leftSplit.toggle()

// 触发右侧边栏
app.workspace.rightSplit.toggle()

// 触发robbion
var e=app, n = e.vault.getConfig("showRibbon");
e.vault.setConfig("showRibbon", !n),
e.updateRibbonDisplay(),
e.workspace.updateFrameless()

// 放大
var e = electron.webFrame.getZoomLevel();
e < 3 && electron.webFrame.setZoomLevel(e + .5)

// 缩小
var e = electron.webFrame.getZoomLevel();
e > -2.5 && electron.webFrame.setZoomLevel(e - .5)

// 复原
electron.webFrame.setZoomLevel(0)

// 新标签新建笔记
app.fileManager.createAndOpenMarkdownFile("", "tab")

//当前标签新建笔记
app.fileManager.createAndOpenMarkdownFile("", false)


模拟获取文件潜在链接

// 获取编辑器内容
let fileContent = app.workspace.activeLeaf.view.editor.getValue();
// 获取潜在链接,并过滤代码块和已是链接的文本(仅简单对`[[]]`形式的链接过滤)
const mentions = app.vault.getFiles().map(item=>item.basename).filter(item=>fileContent.replace(/`+.*?`+/gs, '').replace(/\[\[.*?\]\]/gs, '').includes(item));
// 替换潜在链接
mentions.forEach(m=>fileContent=fileContent.replace(new RegExp(m, 'g'), `[[${m}]]`));
// 替换当前编辑器内容,需谨慎操作
app.workspace.activeLeaf.view.editor.setValue(fileContent)

触发系统菜单

 navFileTitle.addEventListener('contextmenu', (event: MouseEvent) => {
	if (!currentFile?.path) return;

	const menu = new Menu();
	menu.addItem((item) =>
	  item
		.setSection('action')
		.setTitle('Open in new tab')
		.setIcon('file-plus')
		.onClick(() => {
		  this.focusFile(currentFile, 'tab');
		})
	);
	const file = this.app.vault.getAbstractFileByPath(currentFile?.path);
	this.app.workspace.trigger(
	  'file-menu',
	  menu,
	  file,
	  'link-context-menu',
	);
	menu.showAtPosition({ x: event.clientX, y: event.clientY });
  });

详见:recent-files-obsidian/main.ts at d006dd76a6cb859fd57360aa58bf6323c97088f3 · tgrosinger/recent-files-obsidian · GitHub

触发自定义事件

//定义事件
const myEvent = new Events();
//注册事件
const myEventHander = () => {console.log('MY_EVENT run')};
this.registerEvent(
    myEvent?.on("MY_EVENT", myEventHander)
);
// 取消注册事件 
myEvent?.off("MY_EVENT", myEventHander)
// 触发自定义事件 
myEvent?.trigger("MY_EVENT");

最简单的插件开发模板

// main.js
// 在根目录添加 .hotreload文件可支持热加载
const { Plugin } = require('obsidian')
module.exports = class extends Plugin {
  onload() {
    console.log('myplugin onload')
    // this.registerObsidianProtocolHandler('open-tab', (url)=> {
    //   if (url.action != 'open-tab' || !url.file) return
    //   this.app.workspace.getLeaf('tab').openFile(this.app.metadataCache.getFirstLinkpathDest(url.file, url.file))
    // });
  }
  onunload() {}
}

// manifest.json 文件
{
  "id": "my-plugin",
  "name": "My Plugin",
  "version": "0.0.1",
  "minAppVersion": "1.5.3",
  "description": "Plugin for daily use",
  "isDesktopOnly": false
}

监控文件打开函数

```dataviewjs
// 开始监控文件打开
onSomeFileOpened("all", (leaf, file) => {
	// 打开的文件是dataviewjs脚本所在文件时跳过
	if(dv.current().file.path === file) return;
	// dataview内容区输出
	dv.container.empty();
	dv.paragraph("你刚才打开了 " + file);
	//控制台打印打开的文件
	console.log(file, "opened @", new Date().toLocaleString());
	// 通知显示打开的文件路径
	new Notice(file + " opened.");
});

// 监控文件打开函数
// watches 待监控的文件,支持数组,watches有3种情况
// 当watches==all或空值(即!watches)时,代表监控所有文件
// 当为文件路径或数组时,代表只监控watches中的文件
// 当watches=["exclude", []]时,代表监控所有的文件,但排除watches[1]中的文件
// 注意:当watches内容被修改时,这时候应该重启obsidian,不然相当于开启了两个监控,因为是通过watches的内容来区分是否同一个监控的
// callback 第一个参数打开文件所在的leaf,第二个参数打开的文件路径
function onSomeFileOpened(watches, callback) {
    const timerKey = encodeURIComponent(watches.toString());
	if(!global[timerKey]) global[timerKey] = {};
	const onActiveLeafChange = async (activeLeaf) => {
		// 定时防止无效触发,只取最后一个触发
		if(global[timerKey]?.lastOpenedLeafTimer) clearTimeout(global[timerKey].lastOpenedLeafTimer)
		global[timerKey].lastOpenedLeafTimer = setTimeout(async () => {
	        // 获取文件路径
			let filePath = activeLeaf?.view.getState()?.file
			// 所有文件
			if(watches === "all" || !watches) {
				callback(activeLeaf, filePath);
			} 
			// 排除文件
			else if(watches[0] && watches[0] === "exclude" && watches[1] && watches[1].length > 0) {
				if(!watches[1].includes(filePath)) {
				    callback(activeLeaf, filePath);
				}
			}
			// 允许文件
			else if(watches.includes(filePath)) {
				callback(activeLeaf, filePath);
			}
		}, 42);
	};
	app.workspace.off("active-leaf-change", onActiveLeafChange);
	app.workspace.on('active-leaf-change', onActiveLeafChange);
	onActiveLeafChange(app.workspace.activeLeaf);
}
```

获取多语言文字和字段

// 获取语言类型,输出 'zh', 'en', 'zh-TW'等
i18next.language;
// 获取某种语言下的文字配置
i18next.getDataByLanguage(i18next.language);
// 比如,获取官方同步插件的"注册"按钮的文字
i18next.getDataByLanguage(i18next.language).default.plugins.sync["button-sign-up"];
// ribbon设置按钮文字
i18next.getDataByLanguage(i18next.language).default.interface.settings

//或通过下面方式获取
const Translate = i18next.t.bind(i18next);
Translate("plugins.file-explorer.action-change-sort")

选择文件对话框(仅支持桌面版)

const { dialog } = require('electron').remote;
// 设置只允许选择文件,'openDirectory' 用于选择目录,'multiSelections' 允许多选
const result = dialog.showOpenDialog({ properties: ['openFile'] })
//用户是否取消了选择(`canceled`)以及选择的文件路径数组(`filePaths`)
console.log(result.filePaths);

渲染 Markdown 到 HTML

感谢 @PlayerMiller 大佬提供

const string = `## demo\n**aaaaa**`;
const el = createDiv()
//path 可省略,通常用于处理文档中的相对路径
const path = app.workspace.getActiveFile().path
// ExcalidrawAutomate.obsidian在支持obsidian的插件中可以去掉ExcalidrawAutomate前缀
await ExcalidrawAutomate.obsidian.MarkdownRenderer.render(app, str, el, path)
console.log(el.innerHTML);

渲染 HTML 到 Markdown

obsidian.htmlToMarkdown(htmlContent)

Excalidraw 弹窗

//添加dropdown
let gridFrequency = 20;
const customControls =  (container) => {
  new ea.obsidian.Setting(container)
    .setName(`Major grid frequency`)
    .addDropdown(dropdown => {
      [2,3,4,5,6,7,8,9,10].forEach(grid=>dropdown.addOption(grid,grid));
      dropdown
        .setValue(gridFrequency)
        .onChange(value => {
           gridFrequency = value;
        })
    })
}
// 弹窗输入框
const result = await utils.inputPrompt(
  "Grid size?",
  null,
  "20",
  null,
  1,
  false,
  customControls
);
console.log(result);

advance-uri 获取 URI 参数

app.plugins.plugins["obsidian-advanced-uri"].lastParameters

版本 version

// 返回安装版本
require("electron").remote.app.getVersion()
// 返回obsidian版本
require("electron").ipcRenderer.sendSync("version")
// 更新
require("electron").ipcRenderer.sendSync("update")
// 检查更新
require("electron").ipcRenderer.sendSync("check-update")
// 禁止更新
require("electron").ipcRenderer.sendSync("disable-update")
// 重启
require("electron").ipcRenderer.sendSync("relaunch")

//获取用户目录
window.electron.ipcRenderer.sendSync("documents-dir")
//获取用户桌面目录
window.electron.ipcRenderer.sendSync("desktop-dir")
//打开沙盒仓库
electron.ipcRenderer.sendSync("sandbox")
//查看沙盒仓库路径
electron.ipcRenderer.sendSync("get-sandbox-vault-path")
// 执行js代码,第二个参数userGesture为true则可以将触发视为用户的操作
electron.webFrame.executeJavaScript('alert(1)')
//缩放网页,放大
var e = electron.webFrame.getZoomLevel();
 < 3 && electron.webFrame.setZoomLevel(e + .5)
//缩小
e > -2.5 && electron.webFrame.setZoomLevel(e - .5)
//恢复缩放
electron.webFrame.setZoomLevel(0)
//打开搜索
e.workspace.activeEditor.showSearch(false)
//打开替换
e.workspace.activeEditor.showSearch(true)
//查看仓库列表
electron.ipcRenderer.sendSync("vault-list")
//打开仓库
window.electron.ipcRenderer.sendSync("vault-open", t.path, !1)
// 导出pdf
app.workspace.activeLeaf.view.printToPdf()
// 打开文件恢复窗口
app.internalPlugins.plugins['file-recovery'].instance.openModal()

打开悬浮搜索

// 打开悬浮搜索
// query查询字符串,isInHover是否是用hover editor
async function openFloatSearch(query, isInHover=true) {
    const selected = app.workspace.activeLeaf.view.editor?.getSelection();
	//const leaf = app.workspace.getLeaf('tab');
	// 这种createLeafInParent方式可能有时会不成功
	const leaf = app.workspace.createLeafInParent(app.workspace.floatingSplit);
	await leaf.setViewState({
		type: 'search',
		active: false,
		state: {
			query: query || selected || ""
		}
	});
	if(isInHover) app.plugins.plugins['obsidian-hover-editor'].convertLeafToPopover(leaf);
}
// 加上path:前缀仅搜索当前激活文件
openFloatSearch(`path:${app.workspace.getActiveFile()?.path} ${app.workspace.activeEditor?.getSelection()||""}`);

调用 ob 自带的 Markdown 编辑器,实时渲染 Markdown 示例(官方不推荐)

感谢 @PlayerMiller 大佬提供

//main.js
const createEditor = (app)=> {
    const md = app.embedRegistry.embedByExtension.md({app, containerEl: createDiv()})
    md.load(); md.editable = !0; md.showEditor()
    const mEditor = Object.getPrototypeOf(Object.getPrototypeOf(md.editMode)).constructor
    md.unload()
    return e = new mEditor(app, createDiv(), {
      app, scroll: 0, editMode: null,
      get editor() { return e.editor },
      showSearch() {},
      toggleMode() {},
      onMarkdownScroll() {},
      getMode: ()=> 'source',
      // get file() { return view.file },
      // get path() { return view.file.path },
    })
  }
  const test = (app, ob)=> new class extends ob.Modal {
    constructor(app) { super(app); this.open() }
    onOpen() {
      const e = createEditor(app); e.set('**test**'); e.load()
      e.containerEl.setCssProps({
        width: '200px', height: '100px',
        background: 'var(--background-secondary)',
      })
      this.containerEl.append(e.containerEl)
      this.modalEl.style.display = 'none'
      const value = myEditor.editor.getValue();
      window.myEditor = e;
      console.log(e, value)
    }
  }(app, ob)
  const ob = require('obsidian')
  module.exports = class extends ob.Plugin {
    onload() {
      this.addCommand({
        id: 'new-editor-modal', name: 'New editor modal',
        callback: async ()=> test(this.app, ob),
      })
    }
    onunload() {}
  }

以上仅供参考。

有知道更多api的大佬可继续补充。

5 个赞

调用 ob 自带的 Markdown 编辑器,实时渲染 Markdown 示例

这个官方不让用,我原先那个也已经删掉了。


顺便也放点杂七杂八的上来:

/* Obsidian */
app.workspace.activeLeaf.getViewState()
this.registerExtensions(['mp4', 'mkv'], 'viewtype')
app.metadataCache.getBacklinksForFile(app.workspace.getActiveFile())

/* CodeMirror */
滚动到 = (offset)=> {
  const pos = editor.offsetToPos(offset); editor.setCursor(pos)
  editor.scrollIntoView({
    from: {line: pos.line-10, ch: 0}, to: {line: pos.line+10, ch: 0}
  })
}

/* Array */
arr.reverse()
arr.fill('80px', 0)

/* Element */
el.addEventListener('click', evt=> evt.stopImmediatePropagation(), !0)

/* Dataview */
DataviewAPI.page(app.workspace.getActiveFile().basename)
DataviewAPI.pages(`"${app.workspace.getActiveFile().parent.path}"`)
dv.pages(`"${path}"`).sort(p=> p.file.mtime.ts)
4 个赞

哈哈。感谢大佬!这是以前总结的,时间久了,都不记得哪来的了,看来以后要随手记出处,养成习惯才行。

请教下大佬,不让用,那官方推荐用什么?

1 个赞

很遗憾官方给我的回复是最好就别动它。

1 个赞

哦哦,那估计在插件里是用不了了,除非自用不发布。

1 个赞