插件开发
核心概念
Plugin 基类
所有插件都继承自 Plugin 基类:
typescript
import { Plugin } from 'obsidian';
export default class MyPlugin extends Plugin {
async onload() {
// 插件加载时执行
}
onunload() {
// 插件卸载时执行
}
}生命周期
typescript
async onload() {
// 1. 加载设置
await this.loadSettings();
// 2. 注册命令
this.addCommand({ ... });
// 3. 注册视图
this.registerView({ ... });
// 4. 添加 UI 元素
this.addRibbonIcon({ ... });
// 5. 注册事件监听
this.registerEvent({ ... });
}
onunload() {
// 清理资源
}注册命令
基本命令
typescript
this.addCommand({
id: 'insert-date',
name: 'Insert current date',
callback: () => {
const date = new Date().toLocaleDateString();
// 在光标位置插入文本
const editor = this.app.workspace.activeEditor?.editor;
if (editor) {
editor.replaceSelection(date);
}
}
});带检查器的命令
typescript
this.addCommand({
id: 'format-note',
name: 'Format current note',
checkCallback: (checking: boolean) => {
const file = this.app.workspace.getActiveFile();
if (file) {
if (!checking) {
this.formatFile(file);
}
return true;
}
return false;
}
});UI 元素
功能区图标
typescript
import { Notice } from 'obsidian';
this.addRibbonIcon('calendar', 'Open calendar', (evt) => {
new Notice('Calendar clicked!');
});状态栏
typescript
this.addStatusBarItem().setText('My Plugin Active');视图
typescript
import { ItemView, WorkspaceLeaf } from 'obsidian';
const VIEW_TYPE = 'my-plugin-view';
class MyView extends ItemView {
constructor(leaf: WorkspaceLeaf) {
super(leaf);
}
getViewType() {
return VIEW_TYPE;
}
getDisplayText() {
return 'My View';
}
async onOpen() {
const container = this.containerEl.children[1];
container.empty();
container.createEl('h1', { text: 'Hello from my view' });
}
async onClose() {
// 清理
}
}
// 注册视图
this.registerView(VIEW_TYPE, (leaf) => new MyView(leaf));
// 添加视图按钮
this.addRibbonIcon('info', 'Open my view', () => {
this.activateView();
});
async activateView() {
const { workspace } = this.app;
let leaf: WorkspaceLeaf | null = null;
const leaves = workspace.getLeavesOfType(VIEW_TYPE);
if (leaves.length > 0) {
leaf = leaves[0];
} else {
leaf = workspace.getRightLeaf(false);
await leaf?.setViewState({ type: VIEW_TYPE, active: true });
}
if (leaf) {
workspace.revealLeaf(leaf);
}
}模态框
基本模态框
typescript
import { App, Modal, Notice, Plugin, Setting } from 'obsidian';
class SampleModal extends Modal {
constructor(app: App) {
super(app);
}
onOpen() {
const { contentEl } = this;
contentEl.createEl('h1', { text: 'Modal Title' });
new Setting(contentEl)
.setName('Name')
.addText(text => text.setPlaceholder('Enter name'));
new Setting(contentEl)
.addButton(btn => btn
.setButtonText('Submit')
.setCta()
.onClick(() => {
this.close();
}));
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
}
// 打开模态框
this.addCommand({
id: 'open-modal',
name: 'Open sample modal',
callback: () => {
new SampleModal(this.app).open();
}
});带建议的输入框
typescript
import { App, SuggestModal, TFile } from 'obsidian';
class FileSuggestModal extends SuggestModal<TFile> {
constructor(app: App, onChoose: (file: TFile) => void) {
super(app);
this.onChoose = onChoose;
}
getSuggestions(query: string): TFile[] {
const files = this.app.vault.getMarkdownFiles();
return files.filter(file =>
file.path.toLowerCase().includes(query.toLowerCase())
);
}
renderSuggestion(file: TFile, el: HTMLElement) {
el.createEl('div', { text: file.basename });
el.createEl('small', { text: file.parent?.path });
}
onChooseSuggestion(file: TFile) {
this.onChoose(file);
}
onChoose: (file: TFile) => void;
}事件处理
注册事件
typescript
// 文件事件
this.registerEvent(
this.app.vault.on('create', (file) => {
console.log('File created:', file.path);
})
);
this.registerEvent(
this.app.vault.on('modify', (file) => {
console.log('File modified:', file.path);
})
);
// 工作区事件
this.registerEvent(
this.app.workspace.on('file-open', (file) => {
console.log('File opened:', file?.path);
})
);事件类型
typescript
// Vault 事件
vault.on('create', (file) => { });
vault.on('modify', (file) => { });
vault.on('delete', (file) => { });
vault.on('rename', (file, oldPath) => { });
// Workspace 事件
workspace.on('file-open', (file) => { });
workspace.on('active-leaf-change', (leaf) => { });
workspace.on('layout-change', () => { });文件操作
读取文件
typescript
// 读取文本文件
const content = await this.app.vault.read(file);
// 读取二进制文件
const data = await this.app.vault.readBinary(file);
// 读取配置文件
const config = await this.app.vault.readJson('.obsidian/config.json');写入文件
typescript
// 写入文本文件
await this.app.vault.modify(file, 'New content');
// 创建新文件
await this.app.vault.create('path/to/new.md', 'Initial content');
// 删除文件
await this.app.vault.trash(file, true);文件夹操作
typescript
// 创建文件夹
await this.app.vault.createFolder('new-folder');
// 列出文件夹内容
const files = this.app.vault.getAbstractFileByPath('folder');
// 获取所有 Markdown 文件
const mdFiles = this.app.vault.getMarkdownFiles();编辑器操作
获取编辑器
typescript
const view = this.app.workspace.getActiveViewOfType(MarkdownView);
if (view) {
const editor = view.editor;
// 操作编辑器
}编辑器方法
typescript
// 获取选中文本
const selection = editor.getSelection();
// 替换选中文本
editor.replaceSelection('New text');
// 获取光标位置
const cursor = editor.getCursor();
// 设置光标位置
editor.setCursor({ line: 0, ch: 0 });
// 插入文本
editor.replaceRange('Inserted text', { line: 0, ch: 0 });
// 获取行数
const lineCount = editor.lineCount();
// 获取行内容
const line = editor.getLine(0);设置系统
设置接口
typescript
interface MyPluginSettings {
enableFeature: boolean;
maxItems: number;
templatePath: string;
}
const DEFAULT_SETTINGS: MyPluginSettings = {
enableFeature: true,
maxItems: 10,
templatePath: ''
};加载和保存
typescript
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}设置面板
typescript
class MySettingTab extends PluginSettingTab {
plugin: MyPlugin;
constructor(app: App, plugin: MyPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
new Setting(containerEl)
.setName('Enable feature')
.setDesc('Toggle the main feature')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.enableFeature)
.onChange(async (value) => {
this.plugin.settings.enableFeature = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Max items')
.addSlider(slider => slider
.setLimits(1, 100, 1)
.setValue(this.plugin.settings.maxItems)
.setDynamicTooltip()
.onChange(async (value) => {
this.plugin.settings.maxItems = value;
await this.plugin.saveSettings();
}));
}
}通知
typescript
import { Notice } from 'obsidian';
// 基本通知
new Notice('Hello, World!');
// 设置持续时间(毫秒)
new Notice('This will show for 5 seconds', 5000);
// 错误通知
new Notice('Error occurred!', 0); // 0 表示需要手动关闭下一步
- API 参考 - 查阅完整 API 文档