### Plugin Development Example
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
A basic example demonstrating how to create an Obsidian plugin, including lifecycle methods and basic functionality.
```APIDOC
## Obsidian Plugin Example
### Description
This example shows a minimal Obsidian plugin that logs a message when enabled and cleans up when disabled.
### Method
N/A (This is a plugin structure example)
### Endpoint
N/A
### Parameters
N/A
### Request Example
N/A
### Response
N/A
### Code Example
```typescript
import {
App,
Editor,
EditorPosition,
EditorRange,
EditorSelection,
EditorTransaction,
FuzzyMatcher,
FuzzySuggestModal,
Keymap,
KeymapEvents,
Leaf,
MarkdownPostProcessorContext,
MarkdownRenderer,
Menu,
Modal,
Notice,
ObsidianLinkView,
PaneType,
Plugin,
PluginSettingTab,
RequestUrlParam,
RequestUrlResponse,
Setting,
Socket,
TAbstractFile,
TFile,
TFolder,
View,
Workspace,
WorkspaceLeaf,
addIcon,
debounce,
getLinkpath,
getMarkdownStartingLine,
getPluginById,
getTemplater,
getVault,
handleLinkClick,
htmlToMarkdown,
loadMathJax,
loadPrism,
parseLinkHeader,
parseObsidianUrl,
parseYaml,
request,
requestUrl,
setIcon,
setObsidianTheme,
stripObsidianUrl,
swap,
time,
unloadImage
} from 'obsidian';
// Remember to declare the settings for your plugin
interface MyPluginSettings {
mySetting: string;
}
const DEFAULT_SETTINGS: MyPluginSettings = {
mySetting: 'default'
}
export default class MyPlugin extends Plugin {
settings: MyPluginSettings;
author: 'Your Name';
name: 'My Plugin';
id: 'my-plugin';
async onload() {
console.log('Loading plugin: My Plugin');
await this.loadSettings();
// This creates a new setting and adds a button to the settings tab.
this.addSettingTab(new SampleSettingTab(this));
// If the plugin is remounted after an hot reload then the `onunload` hook for the previous version
// will be in memory. Next time we load the current plugin the `onunload` hook will be called twice.
// Therefore we need to check if the plugin is already loaded.
this.registerInterval(window.setInterval(() => console.log('this is my plugin running'), 10 * 1000));
}
autoload() {
console.log('Autoloading plugin: My Plugin');
}
onunload() {
console.log('Unloading plugin: My Plugin');
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
}
class SampleSettingTab extends PluginSettingTab {
plugin: MyPlugin;
constructor(app: App, plugin: MyPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
let {
containerEl
} = this;
containerEl.empty();
containerEl.createEl('h2', {
text: 'Settings for my awesome plugin.'
});
new Setting(containerEl)
.setName('My setting')
.setDesc('This describes my setting')
.addText(text => text
.setPlaceholder('Enter your setting')
.setValue(this.plugin.settings.mySetting)
.onChange(async (value) => {
this.plugin.settings.mySetting = value;
await this.plugin.saveSettings();
}));
}
}
```
```
--------------------------------
### Obsidian FileManager: File Operations and Frontmatter
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Demonstrates various file operations using Obsidian's FileManager, such as renaming files, generating markdown links, determining new file locations, processing frontmatter atomically, managing attachment paths, and prompting for deletion. This example utilizes the Obsidian API within a plugin context.
```typescript
import { Plugin, TFile } from 'obsidian';
export default class FileManagerExamplePlugin extends Plugin {
async onload() {
const file = this.app.vault.getFileByPath('my-note.md');
if (file) {
// Rename a file (automatically updates all links)
await this.app.fileManager.renameFile(file, 'new-folder/new-name.md');
// Generate a markdown link respecting user preferences
const link = this.app.fileManager.generateMarkdownLink(
file,
'source-note.md', // sourcePath
'#heading', // subpath (optional)
'Display Text' // alias (optional)
);
console.log('Generated link:', link);
// Get the folder for new files based on user settings
const newFileParent = this.app.fileManager.getNewFileParent(
'current-note.md', // sourcePath
'new-note.md' // newFilePath
);
console.log('New file parent:', newFileParent.path);
// Process frontmatter atomically
await this.app.fileManager.processFrontMatter(file, (frontmatter) => {
// Modify frontmatter directly
frontmatter['tags'] = frontmatter['tags'] || [];
frontmatter['tags'].push('new-tag');
frontmatter['date'] = new Date().toISOString().split('T')[0];
// Delete a property
delete frontmatter['obsolete-property'];
});
// Get path for saving attachments
const attachmentPath = await this.app.fileManager.getAvailablePathForAttachment(
'image.png',
file.path
);
console.log('Attachment should be saved to:', attachmentPath);
// Prompt user for deletion confirmation
const confirmed = await this.app.fileManager.promptForDeletion(file);
if (confirmed) {
// User confirmed deletion
await this.app.fileManager.trashFile(file);
}
}
}
}
```
--------------------------------
### Obsidian API Utility Functions
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Provides examples of using various utility functions from the Obsidian API for common tasks. Includes path normalization, fuzzy and simple search, debouncing, HTTP requests, YAML processing, markdown rendering, icon management, and link parsing.
```typescript
import {
Plugin,
normalizePath,
prepareFuzzySearch,
prepareSimpleSearch,
debounce,
requestUrl,
request,
parseYaml,
stringifyYaml,
htmlToMarkdown,
MarkdownRenderer,
setIcon,
addIcon,
getIcon,
Notice,
setTooltip,
getLinkpath,
parseLinktext,
getAllTags,
getFrontMatterInfo,
Platform
} from 'obsidian';
export default class UtilityExamplePlugin extends Plugin {
async onload() {
// Path normalization
const normalized = normalizePath('folder//subfolder\file.md');
console.log('Normalized path:', normalized); // 'folder/subfolder/file.md'
// Fuzzy search
const fuzzySearch = prepareFuzzySearch('hlo wrd');
const result1 = fuzzySearch('Hello World');
if (result1) {
console.log('Fuzzy match score:', result1.score);
console.log('Match positions:', result1.matches);
}
// Simple search (word-based)
const simpleSearch = prepareSimpleSearch('hello world');
const result2 = simpleSearch('Hello beautiful World');
if (result2) {
console.log('Simple match found');
}
// Debounce
const debouncedSave = debounce((text: string) => {
console.log('Saving:', text);
}, 1000, true);
debouncedSave('first');
debouncedSave('second'); // Only 'second' will be logged after 1 second
// HTTP requests
const response = await requestUrl({
url: 'https://api.example.com/data',
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
console.log('Response status:', response.status);
console.log('Response JSON:', response.json);
// Simple text request
const text = await request('https://example.com');
console.log('Response text:', text);
// YAML parsing
const yamlString = `
title: My Note
tags:
- important
- todo
`;
const yamlData = parseYaml(yamlString);
console.log('Parsed YAML:', yamlData);
const backToYaml = stringifyYaml(yamlData);
console.log('Stringified YAML:', backToYaml);
// HTML to Markdown conversion
const html = '
Title
Paragraph with bold text.
';
const markdown = htmlToMarkdown(html);
console.log('Converted markdown:', markdown);
// Render markdown to HTML
const container = createDiv();
await MarkdownRenderer.render(
this.app,
'**Bold** and *italic*',
container,
'',
this
);
console.log('Rendered HTML:', container.innerHTML);
// Icons
addIcon('my-custom-icon', '');
const iconEl = createDiv();
setIcon(iconEl, 'folder'); // Built-in icon
setIcon(iconEl, 'my-custom-icon'); // Custom icon
const svg = getIcon('file');
if (svg) {
console.log('Got SVG element for file icon');
}
// Tooltips
const button = createEl('button', { text: 'Hover me' });
setTooltip(button, 'This is a tooltip', { placement: 'top' });
// Link parsing
const linkPath = getLinkpath('Note Name#Heading');
console.log('Link path:', linkPath); // 'Note Name'
const { path, subpath } = parseLinktext('Note Name#Heading');
console.log('Path:', path, 'Subpath:', subpath);
// Get frontmatter info from text
const fileContent = `---
title: Test
---
`;
const frontmatterInfo = getFrontMatterInfo(fileContent);
if (frontmatterInfo) {
console.log('Frontmatter:', frontmatterInfo.frontmatter);
console.log('Content:', frontmatterInfo.content);
}
// Get all tags from a file
const tags = getAllTags(this.app.workspace.getActiveFile());
console.log('Tags:', tags);
// Platform check
if (Platform.isMobile) {
console.log('Running on mobile');
}
}
}
```
--------------------------------
### Manipulate Editor Content and Cursor with TypeScript
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Demonstrates how to use the Obsidian Editor API to perform common tasks such as reading and writing document content, managing cursor positions, handling selections, and executing editor commands. This example shows how to integrate these operations within an Obsidian plugin command.
```typescript
import { Plugin, Editor, MarkdownView, EditorPosition, EditorSelection } from 'obsidian';
export default class EditorExamplePlugin extends Plugin {
async onload() {
this.addCommand({
id: 'editor-manipulation',
name: 'Editor Manipulation Example',
editorCallback: (editor: Editor, view: MarkdownView) => {
const content = editor.getValue();
const cursor: EditorPosition = editor.getCursor();
editor.setCursor({ line: 0, ch: 0 });
editor.replaceSelection(`**${editor.getSelection()}**`);
editor.transaction({
changes: [
{ from: { line: 0, ch: 0 }, to: { line: 0, ch: 5 }, text: 'Hello' },
{ from: { line: 1, ch: 0 }, text: 'New line\n' }
],
selection: { anchor: { line: 0, ch: 5 } }
});
editor.exec('indentMore');
}
});
}
}
```
--------------------------------
### Parse Frontmatter and Content - TypeScript
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
This snippet demonstrates how to parse frontmatter information from file content using the `getFrontMatterInfo` function. It logs whether frontmatter exists, its content, and the starting position of the main content. This is useful for plugins that need to read or modify metadata at the beginning of a file.
```typescript
const fmInfo = getFrontMatterInfo(fileContent);
console.log('Has frontmatter:', fmInfo.exists);
console.log('Frontmatter:', fmInfo.frontmatter);
console.log('Content starts at:', fmInfo.contentStart);
```
--------------------------------
### Extend Obsidian Context Menus with TypeScript
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Demonstrates how to register event listeners for 'file-menu' and 'editor-menu' to inject custom actions. It also shows how to instantiate a standalone Menu object and display it at specific screen coordinates.
```typescript
import { Plugin, Menu, TFile, Notice } from 'obsidian';
export default class MenuExamplePlugin extends Plugin {
async onload() {
this.registerEvent(
this.app.workspace.on('file-menu', (menu: Menu, file: TFile, source: string) => {
menu.addItem((item) => {
item
.setTitle('Copy file path')
.setIcon('copy')
.onClick(() => {
navigator.clipboard.writeText(file.path);
new Notice('Path copied!');
});
});
menu.addSeparator();
menu.addItem((item) => {
item
.setTitle('Delete (dangerous)')
.setIcon('trash')
.setWarning(true)
.onClick(() => {});
});
})
);
this.registerEvent(
this.app.workspace.on('editor-menu', (menu, editor, view) => {
const selection = editor.getSelection();
if (selection) {
menu.addItem((item) => {
item
.setTitle('Make bold')
.setIcon('bold')
.onClick(() => {
editor.replaceSelection(`**${selection}**`);
});
});
}
})
);
this.addCommand({
id: 'show-custom-menu',
name: 'Show Custom Menu',
callback: () => {
const menu = new Menu();
menu.addItem((item) => {
item.setTitle('Option 1').setIcon('file').onClick(() => { new Notice('Option 1 selected'); });
});
menu.addItem((item) => {
item.setTitle('Option 2').setIcon('folder').setChecked(true).onClick(() => { new Notice('Option 2 selected'); });
});
menu.addItem((item) => { item.setTitle('Disabled option').setDisabled(true); });
menu.addSeparator();
menu.addItem((item) => { item.setTitle('Section Label').setIsLabel(true); });
menu.showAtPosition({ x: 100, y: 100 });
}
});
}
}
```
--------------------------------
### Create Custom View and Manage Workspace in Obsidian
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Demonstrates how to define a custom ItemView, register it within a plugin, and perform various workspace operations such as opening files in different layouts and listening to UI events.
```typescript
import { Plugin, MarkdownView, WorkspaceLeaf, TFile, ItemView } from 'obsidian';
const VIEW_TYPE_EXAMPLE = 'example-view';
class ExampleView extends ItemView {
getViewType(): string {
return VIEW_TYPE_EXAMPLE;
}
getDisplayText(): string {
return 'Example View';
}
async onOpen() {
const container = this.containerEl.children[1];
container.empty();
container.createEl('h4', { text: 'Example View' });
container.createEl('p', { text: 'This is a custom view!' });
}
async onClose() {}
}
export default class WorkspaceExamplePlugin extends Plugin {
async onload() {
this.registerView(VIEW_TYPE_EXAMPLE, (leaf) => new ExampleView(leaf));
this.addCommand({
id: 'open-example-view',
name: 'Open Example View',
callback: () => this.activateView()
});
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (activeView) {
console.log('Active file:', activeView.file?.path);
}
this.registerEvent(this.app.workspace.on('file-open', (file: TFile | null) => {
if (file) console.log('Opened file:', file.path);
}));
}
async activateView() {
const { workspace } = this.app;
let leaf = workspace.getLeavesOfType(VIEW_TYPE_EXAMPLE)[0];
if (!leaf) {
const rightLeaf = workspace.getRightLeaf(false);
if (rightLeaf) {
leaf = rightLeaf;
await leaf.setViewState({ type: VIEW_TYPE_EXAMPLE, active: true });
}
}
if (leaf) workspace.revealLeaf(leaf);
}
}
```
--------------------------------
### Perform Vault File and Folder Operations in Obsidian
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Demonstrates how to interact with the Obsidian Vault API to read, modify, create, move, and delete files. It also illustrates how to traverse folder structures and register event listeners for vault changes.
```typescript
import { Plugin, TFile, TFolder, TAbstractFile } from 'obsidian';
export default class VaultExamplePlugin extends Plugin {
async onload() {
const markdownFiles: TFile[] = this.app.vault.getMarkdownFiles();
const file = this.app.vault.getFileByPath('folder/note.md');
if (file) {
const content = await this.app.vault.read(file);
await this.app.vault.modify(file, content + '\n\nAppended text');
await this.app.vault.process(file, (data) => data.replace('old text', 'new text'));
}
const newFile = await this.app.vault.create('new-folder/new-note.md', '# New Note\n\nContent here');
await this.app.vault.createFolder('my-new-folder');
await this.app.vault.rename(newFile, 'another-folder/renamed-note.md');
await this.app.vault.trash(newFile, true);
const folder = this.app.vault.getFolderByPath('my-folder');
if (folder) {
Vault.recurseChildren(folder, (file: TAbstractFile) => {
console.log(file.path);
});
}
this.registerEvent(this.app.vault.on('create', (file: TAbstractFile) => console.log('Created:', file.path)));
}
}
```
--------------------------------
### Obsidian Plugin Base Class Implementation (TypeScript)
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Demonstrates the structure of an Obsidian plugin by extending the base `Plugin` class. Includes initialization (`onload`), settings management (`loadSettings`, `saveSettings`), ribbon icon, status bar item, command registration, settings tab, code block processing, and event listening for file creation.
```typescript
import { Plugin, PluginSettingTab, Setting, Notice, App, TFile, MarkdownView } from 'obsidian';
interface MyPluginSettings {
greeting: string;
enableFeature: boolean;
}
const DEFAULT_SETTINGS: MyPluginSettings = {
greeting: 'Hello',
enableFeature: true
};
export default class MyPlugin extends Plugin {
settings: MyPluginSettings;
async onload() {
// Load saved settings
await this.loadSettings();
// Add a ribbon icon (left sidebar)
this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
new Notice('This is a notice!');
});
// Add a status bar item (bottom bar, desktop only)
const statusBarItem = this.addStatusBarItem();
statusBarItem.setText('Plugin loaded');
// Register a global command
this.addCommand({
id: 'open-sample-modal',
name: 'Open Sample Modal',
callback: () => {
new Notice(`${this.settings.greeting}, World!`);
}
});
// Register an editor command (only available when editing)
this.addCommand({
id: 'insert-text',
name: 'Insert text at cursor',
editorCallback: (editor, view) => {
editor.replaceSelection('Inserted text');
}
});
// Register a settings tab
this.addSettingTab(new MyPluginSettingTab(this.app, this));
// Register a code block processor
this.registerMarkdownCodeBlockProcessor('sample', (source, el, ctx) => {
el.createEl('pre', { text: source.toUpperCase() });
});
// Listen to file events
this.registerEvent(
this.app.vault.on('create', (file) => {
console.log('File created:', file.path);
})
);
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
}
class MyPluginSettingTab 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('Greeting')
.setDesc('The greeting to use')
.addText(text => text
.setPlaceholder('Enter greeting')
.setValue(this.plugin.settings.greeting)
.onChange(async (value) => {
this.plugin.settings.greeting = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Enable Feature')
.setDesc('Toggle the feature on/off')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.enableFeature)
.onChange(async (value) => {
this.plugin.settings.enableFeature = value;
await this.plugin.saveSettings();
}));
}
}
```
--------------------------------
### Fuzzy Suggest Modal for File Selection in TypeScript
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Implements a modal dialog with fuzzy search capabilities for selecting items, specifically demonstrated with file paths. It extends `FuzzySuggestModal` and requires methods to provide items, display them, and handle selection. Ideal for searching through large lists.
```typescript
import { Plugin, FuzzySuggestModal, App } from 'obsidian';
class FileFuzzyModal extends FuzzySuggestModal {
files: string[];
onChoose: (file: string) => void;
constructor(app: App, files: string[], onChoose: (file: string) => void) {
super(app);
this.files = files;
this.onChoose = onChoose;
}
getItems(): string[] {
return this.files;
}
getItemText(item: string): string {
return item;
}
onChooseItem(item: string, evt: MouseEvent | KeyboardEvent) {
this.onChoose(item);
}
}
// Example usage within a plugin:
// const files = this.app.vault.getMarkdownFiles().map(f => f.path);
// new FileFuzzyModal(this.app, files, (file) => {
// console.log('Selected file:', file);
// }).open();
```
--------------------------------
### Create Canvas File with Obsidian API
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Demonstrates how to create a new canvas file in Obsidian using the Vault API. It defines canvas data including nodes and edges and then saves it as a JSON file.
```typescript
import {
App,
Plugin,
PluginSettingTab,
Setting,
CanvasNode,
CanvasEdge,
CanvasData,
CanvasEdgeData
} from 'obsidian';
interface MyPluginSettings {
mySetting: string;
}
export default class MyPlugin extends Plugin {
settings: MyPluginSettings;
async onload() {
await this.loadSettings();
this.addCommand({
id: 'create-canvas',
name: 'Create Canvas',
callback: async () => {
await this.createCanvas();
},
hotkeys: [
{
modifiers: ['Mod'],
key: 'n',
}
]
});
this.addSettingTab(new SettingTab(this.app, this));
}
onunload() {
console.log('Unloading plugin');
}
async loadSettings() {
this.settings = Object.assign({}, {
mySetting: 'default-value'
}, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
async createCanvas() {
const canvasData: CanvasData = {
nodes: [
{
id: 'node1',
type: 'text',
position: { x: 0, y: 0 },
text: 'Node 1'
},
{
id: 'node2',
type: 'text',
position: { x: 200, y: 100 },
text: 'Node 2'
},
{
id: 'node3',
type: 'text',
position: { x: 400, y: 200 },
text: 'Node 3'
}
],
edges: [
{
id: 'edge1',
fromNode: 'node1',
fromSide: 'bottom',
toNode: 'node3',
toSide: 'top',
fromEnd: 'none',
toEnd: 'arrow'
} as CanvasEdgeData
]
};
await this.app.vault.create(
'Generated Canvas.canvas',
JSON.stringify(canvasData, null, 2)
);
}
}
class SettingTab extends PluginSettingTab {
plugin: MyPlugin;
constructor(app: App, plugin: MyPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const {
containerEl
} = this;
containerEl.empty();
containerEl.createEl('h2', { text: 'Settings for my plugin' });
new Setting(containerEl)
.setName('Setting #1')
.setDesc('Enter your setting here')
.addText(text => text
.setPlaceholder('Enter your value')
.setValue(this.plugin.settings.mySetting)
.onChange(async (value) => {
this.plugin.settings.mySetting = value;
await this.plugin.saveSettings();
}));
}
}
```
--------------------------------
### Workspace View and Layout Management
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Methods for managing custom views, opening files in specific panes, and interacting with the workspace layout.
```APIDOC
## Workspace UI Management
### Description
Methods to register custom views, open files in various pane configurations (tabs, splits, windows), and manage sidebars.
### Methods
- `registerView(type, callback)`: Registers a custom view type.
- `getLeaf(type, direction)`: Creates or retrieves a leaf (tab/pane).
- `openFile(file)`: Opens a file in a specific leaf.
- `getLeavesOfType(type)`: Retrieves all leaves of a specific view type.
### Request Example
// Open a file in a new tab
const leaf = this.app.workspace.getLeaf('tab');
await leaf.openFile(file);
### Response
Returns a `WorkspaceLeaf` object representing the created or retrieved pane.
```
--------------------------------
### Display Notices - TypeScript
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
This snippet illustrates how to display user notifications within Obsidian using the `Notice` class. It shows how to create simple text notices, notices with a specified duration, and notices containing custom HTML fragments created with `createFragment`. This is essential for providing feedback to the user.
```typescript
// Notices
new Notice('Simple notice');
new Notice('Notice with duration', 5000); // 5 seconds
const fragment = createFragment((frag) => {
frag.createEl('strong', { text: 'Bold notice' });
frag.createEl('br');
frag.appendText('With multiple lines');
});
new Notice(fragment);
```
--------------------------------
### Basic Modal Dialog in TypeScript
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Implements a simple modal dialog for user input. It uses the `Modal` class and `Setting` for UI elements. Accepts user input and returns it via a callback.
```typescript
import { Plugin, Modal, App, Setting } from 'obsidian';
class ExampleModal extends Modal {
result: string;
onSubmit: (result: string) => void;
constructor(app: App, onSubmit: (result: string) => void) {
super(app);
this.onSubmit = onSubmit;
}
onOpen() {
const { contentEl } = this;
contentEl.createEl('h1', { text: 'Enter your name' });
new Setting(contentEl)
.setName('Name')
.addText((text) =>
text.onChange((value) => {
this.result = value;
})
);
new Setting(contentEl)
.addButton((btn) =>
btn
.setButtonText('Submit')
.setCta()
.onClick(() => {
this.close();
this.onSubmit(this.result);
})
);
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
}
// Example usage within a plugin:
// new ExampleModal(this.app, (result) => {
// console.log('User entered:', result);
// }).open();
```
--------------------------------
### Accessing and Monitoring File Metadata in Obsidian
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Demonstrates how to retrieve cached metadata for a specific file, including headings, links, tags, and frontmatter. It also shows how to resolve link paths and register event listeners for metadata changes.
```typescript
import { Plugin, TFile, CachedMetadata, getAllTags, parseFrontMatterTags, parseFrontMatterAliases } from 'obsidian';
export default class MetadataCacheExamplePlugin extends Plugin {
async onload() {
const file = this.app.vault.getFileByPath('my-note.md');
if (file) {
const cache: CachedMetadata | null = this.app.metadataCache.getFileCache(file);
if (cache) {
if (cache.headings) {
cache.headings.forEach(heading => {
console.log(`H${heading.level}: ${heading.heading}`);
});
}
const allTags = getAllTags(cache);
console.log('All tags:', allTags);
if (cache.frontmatter) {
const fmTags = parseFrontMatterTags(cache.frontmatter);
console.log('Frontmatter tags:', fmTags);
}
}
}
const linkedFile = this.app.metadataCache.getFirstLinkpathDest('Note Name', 'current-folder/current-note.md');
this.registerEvent(
this.app.metadataCache.on('changed', (file: TFile, data: string, cache: CachedMetadata) => {
console.log('Metadata changed for:', file.path);
})
);
}
}
```
--------------------------------
### Platform Detection - TypeScript
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
This code snippet shows how to detect the current operating system and environment using the `Platform` object. It checks for desktop, mobile, macOS, Windows, Linux, iOS app, and Android app environments. This is crucial for creating platform-specific behaviors or UI adjustments in Obsidian plugins.
```typescript
// Platform detection
console.log('Is desktop:', Platform.isDesktop);
console.log('Is mobile:', Platform.isMobile);
console.log('Is macOS:', Platform.isMacOS);
console.log('Is Windows:', Platform.isWin);
console.log('Is Linux:', Platform.isLinux);
console.log('Is iOS app:', Platform.isIosApp);
console.log('Is Android app:', Platform.isAndroidApp);
```
--------------------------------
### Highlight TODOs and Access Context in Obsidian
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Demonstrates two general markdown post-processing tasks: highlighting code blocks containing 'TODO' comments by adding a CSS class and styling, and accessing rendering context information such as section details, frontmatter, and the source file path.
```typescript
import { Plugin, MarkdownPostProcessorContext } from 'obsidian';
export default class PostProcessorExamplePlugin extends Plugin {
async onload() {
// Register a general post processor
this.registerMarkdownPostProcessor((el, ctx) => {
// Find all code elements and highlight TODO comments
const codeBlocks = el.findAll('code');
codeBlocks.forEach(code => {
if (code.textContent?.includes('TODO')) {
code.addClass('has-todo');
code.style.backgroundColor = 'yellow';
}
});
});
// Post processor with section info
this.registerMarkdownPostProcessor((el, ctx) => {
const sectionInfo = ctx.getSectionInfo(el);
if (sectionInfo) {
console.log(`Processing lines ${sectionInfo.lineStart}-${sectionInfo.lineEnd}`);
console.log('Section text:', sectionInfo.text);
}
// Access frontmatter
if (ctx.frontmatter) {
console.log('Frontmatter:', ctx.frontmatter);
}
// Get source path for resolving links
console.log('Source path:', ctx.sourcePath);
});
}
}
```
--------------------------------
### Obsidian Plugin Command Registration in TypeScript
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Demonstrates how to register commands within an Obsidian plugin to trigger modal dialogs. It uses the `addCommand` method of the `Plugin` class, associating a command ID and name with a callback function that opens a specific modal.
```typescript
import { Plugin, Modal, App, Setting, SuggestModal, FuzzySuggestModal, FuzzyMatch } from 'obsidian';
// ... (Modal class definitions would be here or imported)
export default class ModalExamplePlugin extends Plugin {
async onload() {
this.addCommand({
id: 'open-basic-modal',
name: 'Open Basic Modal',
callback: () => {
// Assuming ExampleModal is defined elsewhere or above
new ExampleModal(this.app, (result) => {
console.log('User entered:', result);
}).open();
}
});
this.addCommand({
id: 'open-suggest-modal',
name: 'Open Suggest Modal',
callback: () => {
// Assuming BookSuggestModal is defined elsewhere or above
const books: Book[] = [
{ title: '1984', author: 'George Orwell' },
{ title: 'Brave New World', author: 'Aldous Huxley' },
{ title: 'Fahrenheit 451', author: 'Ray Bradbury' }
];
new BookSuggestModal(this.app, books, (book) => {
console.log('Selected:', book.title);
}).open();
}
});
this.addCommand({
id: 'open-fuzzy-modal',
name: 'Open Fuzzy Modal',
callback: () => {
// Assuming FileFuzzyModal is defined elsewhere or above
const files = this.app.vault.getMarkdownFiles().map(f => f.path);
new FileFuzzyModal(this.app, files, (file) => {
console.log('Selected file:', file);
}).open();
}
});
}
}
```
--------------------------------
### Suggest Modal for Item Selection in TypeScript
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Creates a modal dialog that suggests items based on user input, featuring filtering capabilities. It extends `SuggestModal` and requires methods for fetching suggestions and rendering them. Useful for selecting from a list of items.
```typescript
import { Plugin, SuggestModal, App } from 'obsidian';
interface Book {
title: string;
author: string;
}
class BookSuggestModal extends SuggestModal {
books: Book[];
onChoose: (book: Book) => void;
constructor(app: App, books: Book[], onChoose: (book: Book) => void) {
super(app);
this.books = books;
this.onChoose = onChoose;
}
getSuggestions(query: string): Book[] {
return this.books.filter((book) =>
book.title.toLowerCase().includes(query.toLowerCase()) ||
book.author.toLowerCase().includes(query.toLowerCase())
);
}
renderSuggestion(book: Book, el: HTMLElement) {
el.createEl('div', { text: book.title });
el.createEl('small', { text: book.author });
}
onChooseSuggestion(book: Book, evt: MouseEvent | KeyboardEvent) {
this.onChoose(book);
}
}
// Example usage within a plugin:
// const books: Book[] = [
// { title: '1984', author: 'George Orwell' },
// { title: 'Brave New World', author: 'Aldous Huxley' },
// { title: 'Fahrenheit 451', author: 'Ray Bradbury' }
// ];
// new BookSuggestModal(this.app, books, (book) => {
// console.log('Selected:', book.title);
// }).open();
```
--------------------------------
### Workspace Event Listeners
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Methods for subscribing to workspace lifecycle events such as file opening, layout changes, and active leaf transitions.
```APIDOC
## Workspace Events
### Description
Listen to workspace events to trigger plugin logic when the user interacts with the UI.
### Events
- `file-open`: Triggered when a file is opened.
- `active-leaf-change`: Triggered when the user switches tabs.
- `layout-change`: Triggered when the UI layout is modified.
### Request Example
this.registerEvent(
this.app.workspace.on('file-open', (file) => {
console.log('Opened file:', file?.path);
})
);
```
--------------------------------
### Plugin Class - Base Class for All Plugins
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
The `Plugin` class is the foundation for creating Obsidian plugins. It provides lifecycle hooks, command registration, settings management, and integration with Obsidian's core features. Every plugin must extend this class and export it as the default export.
```APIDOC
## Plugin Class - Base Class for All Plugins
### Description
The `Plugin` class is the foundation for creating Obsidian plugins. It provides lifecycle hooks, command registration, settings management, and integration with Obsidian's core features. Every plugin must extend this class and export it as the default export.
### Method
```typescript
export default class MyPlugin extends Plugin {
settings: MyPluginSettings;
async onload() {
// Load saved settings
await this.loadSettings();
// Add a ribbon icon (left sidebar)
this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
new Notice('This is a notice!');
});
// Add a status bar item (bottom bar, desktop only)
const statusBarItem = this.addStatusBarItem();
statusBarItem.setText('Plugin loaded');
// Register a global command
this.addCommand({
id: 'open-sample-modal',
name: 'Open Sample Modal',
callback: () => {
new Notice(`${this.settings.greeting}, World!`);
}
});
// Register an editor command (only available when editing)
this.addCommand({
id: 'insert-text',
name: 'Insert text at cursor',
editorCallback: (editor, view) => {
editor.replaceSelection('Inserted text');
}
});
// Register a settings tab
this.addSettingTab(new MyPluginSettingTab(this.app, this));
// Register a code block processor
this.registerMarkdownCodeBlockProcessor('sample', (source, el, ctx) => {
el.createEl('pre', { text: source.toUpperCase() });
});
// Listen to file events
this.registerEvent(
this.app.vault.on('create', (file) => {
console.log('File created:', file.path);
})
);
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
}
class MyPluginSettingTab 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('Greeting')
.setDesc('The greeting to use')
.addText(text => text
.setPlaceholder('Enter greeting')
.setValue(this.plugin.settings.greeting)
.onChange(async (value) => {
this.plugin.settings.greeting = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Enable Feature')
.setDesc('Toggle the feature on/off')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.enableFeature)
.onChange(async (value) => {
this.plugin.settings.enableFeature = value;
await this.plugin.saveSettings();
}));
}
}
```
```
--------------------------------
### Create Canvas Programmatically
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
This snippet shows how to construct a CanvasData object from scratch by defining nodes and edges. It demonstrates how to cast objects to specific types like CanvasTextData or CanvasFileData to ensure structure compliance.
```typescript
const canvasData: CanvasData = {
nodes: [
{ id: 'node1', type: 'text', text: '# Main Idea', x: 0, y: 0, width: 300, height: 200, color: '1' } as CanvasTextData,
{ id: 'node2', type: 'file', file: 'My Notes/Important.md', x: 400, y: 0, width: 300, height: 200 } as CanvasFileData
],
edges: [
{ id: 'edge1', fromNode: 'node1', fromSide: 'right', toNode: 'node2', toSide: 'left', toEnd: 'arrow', label: 'relates to' } as CanvasEdgeData
]
};
```
--------------------------------
### Editor Interface API
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Methods for interacting with the Obsidian editor instance, including content manipulation, cursor control, and selection management.
```APIDOC
## Editor Interface Methods
### Description
The Editor interface provides a set of methods to programmatically interact with the active markdown editor, allowing plugins to read and modify document content, manage selections, and control the view.
### Methods
- **getValue()**: Returns the full content of the document.
- **setValue(content: string)**: Replaces the entire document content.
- **getCursor()**: Returns the current cursor position as {line, ch}.
- **setCursor(pos: EditorPosition)**: Moves the cursor to the specified position.
- **getSelection()**: Returns the currently selected text.
- **replaceSelection(text: string)**: Replaces the current selection with the provided text.
- **replaceRange(text: string, from: EditorPosition, to: EditorPosition)**: Replaces a specific range of text.
- **lineCount()**: Returns the total number of lines in the document.
- **transaction(changes: EditorChange[])**: Performs multiple changes in a single atomic operation.
- **exec(command: string)**: Executes an editor-specific command (e.g., 'indentMore').
### Parameters
#### EditorPosition
- **line** (number) - The line index (0-indexed).
- **ch** (number) - The character index within the line.
### Example
```typescript
// Replace selection with bolded text
const selection = editor.getSelection();
if (selection) {
editor.replaceSelection(`**${selection}**`);
}
// Perform a batch transaction
editor.transaction({
changes: [
{ from: { line: 0, ch: 0 }, to: { line: 0, ch: 5 }, text: 'Hello' }
]
});
```
```
--------------------------------
### Render JSON-based Charts with Obsidian
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Processes 'chart' code blocks by parsing JSON input to render a simple bar chart. It handles invalid JSON input by displaying an error message. This demonstrates dynamic content generation based on structured data.
```typescript
import { Plugin, MarkdownPostProcessorContext } from 'obsidian';
export default class PostProcessorExamplePlugin extends Plugin {
async onload() {
// Register a code block processor for ```chart blocks
this.registerMarkdownCodeBlockProcessor('chart', async (source, el, ctx) => {
try {
const data = JSON.parse(source);
const container = el.createDiv({ cls: 'chart-container' });
// Create a simple bar chart
const maxValue = Math.max(...data.values);
data.labels.forEach((label: string, i: number) => {
const bar = container.createDiv({ cls: 'bar' });
bar.style.width = `${(data.values[i] / maxValue) * 100}%`;
bar.createSpan({ text: `${label}: ${data.values[i]}` });
});
} catch (e) {
el.createEl('pre', { text: 'Invalid JSON for chart' });
}
});
}
}
```
--------------------------------
### Registering Events in Obsidian Plugins
Source: https://github.com/obsidianmd/obsidian-api/blob/master/README.md
Demonstrates how to register various types of events within an Obsidian plugin. The `registerEvent` method is used for general app and workspace events, `registerDomEvent` for DOM events on persistent elements, and `registerInterval` for timed operations. These methods ensure proper cleanup when the plugin unloads.
```javascript
this.registerEvent(app.on('event-name', callback));
```
```javascript
this.registerDomEvent(element, 'click', callback);
```
```javascript
this.registerInterval(setInterval(callback, 1000));
```
--------------------------------
### Create Interactive Counter Component with Obsidian
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
Implements an interactive counter using a custom MarkdownRenderChild component. It registers a 'counter' code block to render this component, allowing users to increment or decrement a displayed count.
```typescript
import { Plugin, MarkdownPostProcessorContext, MarkdownRenderChild } from 'obsidian';
// Example: Interactive counter component
class CounterComponent extends MarkdownRenderChild {
count: number = 0;
constructor(containerEl: HTMLElement) {
super(containerEl);
}
onload() {
this.render();
}
render() {
this.containerEl.empty();
const container = this.containerEl.createDiv({ cls: 'counter-container' });
const display = container.createEl('span', { text: `Count: ${this.count}` });
const decrementBtn = container.createEl('button', { text: '-' });
decrementBtn.addEventListener('click', () => {
this.count--;
display.setText(`Count: ${this.count}`);
});
const incrementBtn = container.createEl('button', { text: '+' });
incrementBtn.addEventListener('click', () => {
this.count++;
display.setText(`Count: ${this.count}`);
});
}
}
export default class PostProcessorExamplePlugin extends Plugin {
async onload() {
// Register a code block processor for ```counter blocks
this.registerMarkdownCodeBlockProcessor('counter', (source, el, ctx) => {
const component = new CounterComponent(el);
ctx.addChild(component);
});
}
}
```
--------------------------------
### Read and Parse Canvas Data
Source: https://context7.com/obsidianmd/obsidian-api/llms.txt
This snippet demonstrates how to locate a .canvas file in the vault, read its content, and parse the JSON into typed CanvasData. It iterates through nodes and edges to extract specific properties based on node types.
```typescript
const canvasFile = this.app.vault.getFiles().find(f => f.extension === 'canvas');
if (canvasFile) {
const content = await this.app.vault.read(canvasFile);
const canvasData: CanvasData = JSON.parse(content);
canvasData.nodes.forEach(node => {
console.log(`Node ${node.id}: ${node.x}, ${node.y}`);
});
}
```