feat: expose public plugin API (refs #227)

Adds a small public API surface on the plugin instance so external
plugins (specifically supercharged_links) can resolve a folder path
to its folder note without reaching into internals.

    app.plugins.plugins['folder-notes']?.api?.getEnabledFolderNote(path)
    // returns TFile | null

getEnabledFolderNote respects the same exclusion and detachment
semantics as the plugin's own UI: it returns null for folders that
wouldn't receive the .has-folder-note class. Consumers stay visually
consistent with folder-notes' own behavior.

The method name makes the gating explicit at the call site —
external plugin authors see "enabled" and know that exclusions/
detachment are honored without needing to read documentation.

Pattern mirrors the existing front-matter-title integration. No
new settings, no migrations, no behavior change for existing users.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jules Omlor 2026-04-24 21:29:14 -04:00
parent 3fce80d6fb
commit 6afe4a732a
No known key found for this signature in database
2 changed files with 57 additions and 0 deletions

54
src/api.ts Normal file
View file

@ -0,0 +1,54 @@
import { TFile } from 'obsidian';
import type FolderNotesPlugin from './main';
import { getFolderNote } from './functions/folderNoteFunctions';
import { getDetachedFolder, getExcludedFolder } from './ExcludeFolders/functions/folderFunctions';
const API_VERSION = '1.0.0';
/**
* Public API exposed by the folder-notes plugin on its instance as `plugin.api`.
*
* External plugins can access it via:
* app.plugins.plugins['folder-notes']?.api
*
* The API is available after folder-notes has finished onload (settings loaded).
* It is safe to call synchronously from any point after layout-ready.
*/
export interface FolderNotesApi {
/** Semver-ish version string for feature detection. */
version: string;
/**
* Returns the folder note TFile for the given folder path only if the
* folder's note is enabled i.e., the folder is not detached and not
* excluded with `disableFolderNote`. Matches the plugin's own UI
* gating (the rules that decide whether `.has-folder-note` gets
* applied in the file explorer).
*
* Returns null if:
* - the folder does not exist or has no folder note
* - the folder is detached
* - the folder is excluded with disableFolderNote
*/
getEnabledFolderNote(folderPath: string): TFile | null;
}
export function getApi(plugin: FolderNotesPlugin): FolderNotesApi {
return {
version: getVersion(),
getEnabledFolderNote: (folderPath) => getEnabledFolderNote(plugin, folderPath),
};
}
function getVersion(): string {
return API_VERSION;
}
function getEnabledFolderNote(plugin: FolderNotesPlugin, folderPath: string): TFile | null {
const note = getFolderNote(plugin, folderPath);
if (!note) return null;
if (getDetachedFolder(plugin, folderPath)) return null;
const excluded = getExcludedFolder(plugin, folderPath, true);
if (excluded?.disableFolderNote) return null;
return note;
}

View file

@ -11,6 +11,7 @@ import {
} from './settings/SettingsTab';
import { Commands } from './Commands';
import type { FileExplorerWorkspaceLeaf } from './globals';
import { getApi, type FolderNotesApi } from './api';
import {
registerFileExplorerObserver, unregisterFileExplorerObserver,
} from './events/MutationObserver';
@ -49,10 +50,12 @@ export default class FolderNotesPlugin extends Plugin {
settingsOpened = false;
askModalCurrentlyOpen = false;
fvIndexDB: FvIndexDB;
api!: FolderNotesApi;
async onload(): Promise<void> {
console.log('loading folder notes plugin');
await this.loadSettings();
this.api = getApi(this);
this.settingsTab = new SettingsTab(this.app, this);
this.addSettingTab(this.settingsTab);
this.saveSettings();