~/office-add-ins/obsidian-plugin-entwickeln/

Obsidian-Plugin entwickeln mit TypeScript

Von der offiziellen Vorlage über Befehle, Einstellungen und Ereignisse bis zur Einreichung im Verzeichnis der Community-Plugins: so entsteht ein Obsidian-Plugin.

Ein Obsidian-Plugin entwickeln Sie in TypeScript auf Basis der offiziellen Beispielvorlage von Obsidian. Das Projekt wird zu einer einzigen main.js gebündelt, die zusammen mit manifest.json im Plugin-Ordner eines Tresors liegt. Ihre Plugin-Klasse meldet in onload() Befehle, Einstellungen und Ereignis-Listener an, und Obsidian räumt alles, was Sie über die Registrierungsmethoden angelegt haben, beim Deaktivieren wieder auf.

Obsidian ist eine Electron-Anwendung. Plugins laufen deshalb mit weitreichenden Rechten im selben Prozess wie die App, ohne die Sandbox, die Sie von Browser-Erweiterungen kennen. Das macht den Einstieg leicht und verlangt zugleich Sorgfalt.

Was Sie vorab brauchen

  • Node.js und npm für Build und Abhängigkeiten.
  • Git und ein GitHub-Konto, denn Vorlage und Veröffentlichung laufen über GitHub.
  • Einen eigenen Test-Tresor. Entwickeln Sie nie in Ihrem echten Notiztresor. Ein fehlerhaftes Plugin kann Dateien verändern oder löschen.
  • Grundkenntnisse in TypeScript. Wie Sie ein Build-Setup selbst aufsetzen, erklärt der Beitrag TypeScript und Vite für Erweiterungen einrichten. Für Obsidian bringt die Vorlage bereits alles mit.

Projekt aus der Vorlage anlegen

  1. Legen Sie einen leeren Tresor an, etwa plugin-test, und öffnen Sie ihn in Obsidian.
  2. Erzeugen Sie auf GitHub aus der Beispielvorlage von Obsidian (Repository obsidian-sample-plugin) ein eigenes Repository und klonen Sie es nach plugin-test/.obsidian/plugins/.
  3. Installieren Sie die Abhängigkeiten mit npm install und starten Sie den Build im Beobachtungsmodus mit npm run dev.
  4. Aktivieren Sie in Obsidian unter Einstellungen die Community-Plugins und schalten Sie Ihr Plugin ein.

Der Ordnername sollte zur id im Manifest passen. Der Build erzeugt main.js direkt im Projektordner, wo Obsidian sie erwartet.

Die Dateien im Überblick

Datei Aufgabe
manifest.json Name, ID, Version, Mindestversion von Obsidian, Beschreibung, Autor
main.ts Quellcode der Plugin-Klasse
main.js gebündeltes Ergebnis, das Obsidian lädt
styles.css optionale Stile, werden automatisch geladen
versions.json ordnet Plugin-Versionen der jeweils nötigen Obsidian-Version zu
esbuild.config.mjs Build-Konfiguration der Vorlage

Ein Manifest sieht etwa so aus:

{
  "id": "tagesnotiz-zaehler",
  "name": "Tagesnotiz-Zähler",
  "version": "1.0.0",
  "minAppVersion": "1.5.0",
  "description": "Zählt Wörter in der aktuellen Notiz und zeigt sie in der Statusleiste.",
  "author": "Redaktion AddOn-Coders",
  "isDesktopOnly": false
}

Die id ist dauerhaft. Sie bestimmt den Ordnernamen bei allen Nutzern und darf nach der Veröffentlichung nicht mehr geändert werden. Die minAppVersion setzen Sie auf die älteste Obsidian-Version, mit der Sie tatsächlich getestet haben.

Die Plugin-Klasse

Jedes Plugin exportiert eine Klasse, die von Plugin erbt. Das folgende Beispiel registriert einen Befehl, ein Symbol in der Seitenleiste und eine Anzeige in der Statusleiste:

import { Notice, Plugin, MarkdownView } from "obsidian";

export default class WortZaehler extends Plugin {
  async onload() {
    const status = this.addStatusBarItem();

    this.addCommand({
      id: "woerter-zaehlen",
      name: "Wörter der aktuellen Notiz zählen",
      editorCallback: (editor) => {
        const anzahl = editor.getValue().split(/\s+/).filter(Boolean).length;
        new Notice(`${anzahl} Wörter`);
      },
    });

    this.addRibbonIcon("hash", "Wörter zählen", () => {
      const view = this.app.workspace.getActiveViewOfType(MarkdownView);
      if (!view) return;
      const anzahl = view.editor.getValue().split(/\s+/).filter(Boolean).length;
      status.setText(`${anzahl} Wörter`);
    });
  }

  onunload() {
    // Über add... und register... angelegte Elemente entfernt Obsidian selbst.
  }
}

Wichtig ist der Unterschied zwischen callback und editorCallback bei Befehlen: editorCallback erscheint nur, wenn ein Editor aktiv ist, und bekommt ihn direkt übergeben. So vermeiden Sie Fehler, wenn gerade keine Notiz geöffnet ist.

Auf Ereignisse reagieren

Viele Plugins reagieren auf Änderungen im Tresor oder im Arbeitsbereich. Registrieren Sie solche Listener immer über this.registerEvent(), damit Obsidian sie beim Deaktivieren entfernt:

this.registerEvent(
  this.app.vault.on("modify", (datei) => {
    console.log("geändert:", datei.path);
  })
);

Dasselbe Prinzip gilt für DOM-Ereignisse (registerDomEvent) und Intervalle (registerInterval). Wer stattdessen direkt addEventListener oder setInterval nutzt, hinterlässt nach dem Deaktivieren laufenden Code. Das ist einer der häufigsten Gründe für Nachfragen bei der Prüfung.

Dateien lesen und schreiben

Für Notizen nutzen Sie die Tresor-API statt direkter Dateisystemzugriffe. app.vault.read() liest eine Datei, app.vault.modify() schreibt sie. Für Änderungen an bestehenden Dateien empfiehlt Obsidian app.vault.process(), das Lesen und Schreiben in einem Schritt erledigt und so Konflikte mit anderen Plugins vermeidet. Frontmatter bearbeiten Sie am sichersten über app.fileManager.processFrontMatter(). Direkte Zugriffe über Node.js funktionieren nur am Desktop und umgehen die Mechanismen, mit denen Obsidian Änderungen verfolgt.

Einstellungen speichern

Einstellungen legt ein Plugin mit loadData() und saveData() in einer data.json in seinem eigenen Ordner ab. Eine eigene Einstellungsseite bauen Sie mit einer Klasse, die von PluginSettingTab erbt:

interface Einstellungen { zielWoerter: number }
const STANDARD: Einstellungen = { zielWoerter: 500 };

// in onload():
this.einstellungen = Object.assign({}, STANDARD, await this.loadData());
this.addSettingTab(new MeineEinstellungen(this.app, this));

In der Einstellungsklasse erzeugen Sie Felder mit new Setting(containerEl), etwa ein Textfeld mit addText() oder einen Schalter mit addToggle(). Speichern Sie bei jeder Änderung mit saveData(). Das Object.assign mit Standardwerten sorgt dafür, dass neue Einstellungen nach einem Update nicht als undefined ankommen.

Markdown erweitern und eigene Ansichten

Viele beliebte Plugins verändern, wie Notizen dargestellt werden. Dafür gibt es zwei Wege. Mit registerMarkdownCodeBlockProcessor() übernehmen Sie die Darstellung eines Codeblocks mit eigener Sprachkennung, etwa zaehler direkt hinter den öffnenden Backticks. Obsidian übergibt Ihnen den Inhalt des Blocks und ein HTML-Element, in das Sie eine Tabelle, ein Diagramm oder eine Auswertung zeichnen. Mit registerMarkdownPostProcessor() bearbeiten Sie die fertige Leseansicht allgemein, etwa um bestimmte Muster hervorzuheben.

Für eigene Bereiche in der Seitenleiste, etwa eine Liste offener Aufgaben, registrieren Sie mit registerView() eine Ansicht, die von ItemView erbt. Die Ansicht öffnen Sie dann über einen Befehl in einem Blatt (Leaf) des Arbeitsbereichs. Beachten Sie, dass die Leseansicht und der Bearbeitungsmodus unterschiedlich funktionieren: Was im Lesemodus über Post-Prozessoren läuft, braucht im Live-Vorschau-Modus des Editors häufig eine Editor-Erweiterung auf Basis von CodeMirror 6. Planen Sie diesen Mehraufwand ein, wenn Ihr Plugin in beiden Modi gleich aussehen soll.

Testen und Fehler finden

Obsidian bringt die Entwicklerwerkzeuge von Chromium mit. Am Desktop öffnen Sie sie über die Tastenkombination Strg+Umschalt+I (macOS: Cmd+Option+I). Dort sehen Sie Konsolenausgaben, Fehler und können Haltepunkte setzen. Hilfreich ist eine kurze Testliste vor jeder Version:

  • Plugin aktivieren, deaktivieren und erneut aktivieren: Bleiben Statusleiste, Symbole oder Listener doppelt zurück?
  • Obsidian ohne geöffnete Notiz starten: Wirft ein Befehl einen Fehler?
  • Mit einem großen Tresor testen: Bremst der Code beim Start?
  • In der mobilen App öffnen, sofern das Plugin nicht als Desktop-only markiert ist.

Starten Sie aufwendige Arbeit nicht direkt in onload(), sondern erst, wenn der Arbeitsbereich bereit ist, etwa über this.app.workspace.onLayoutReady(). So verlängert Ihr Plugin den Start der App nicht.

Veröffentlichen im Verzeichnis der Community-Plugins

  1. Version setzen: Erhöhen Sie version in manifest.json und tragen Sie die Version in versions.json ein. Die Vorlage bringt dafür ein npm-Skript mit.
  2. GitHub-Release anlegen: Der Tag muss genau der Versionsnummer entsprechen, ohne vorangestelltes „v“. Hängen Sie main.js, manifest.json und gegebenenfalls styles.css als einzelne Dateien an.
  3. README und Lizenz ergänzen: Beschreiben Sie Funktion, Bedienung und offen gelegte Netzwerkzugriffe. Eine Lizenzdatei im Repository gehört dazu.
  4. Einreichen: Stellen Sie einen Pull Request an das Repository obsidian-releases, in dem Ihr Plugin in die Liste der Community-Plugins aufgenommen wird. Ein automatischer Check prüft Manifest und Release, danach sieht sich das Obsidian-Team den Code an.

Spätere Updates brauchen keinen neuen Pull Request. Obsidian liest die Versionen aus Ihren GitHub-Releases. Ein neues Release mit höherer Versionsnummer genügt.

Häufige Gründe für Rückfragen

Problem Besser
innerHTML mit Nutzereingaben DOM-Hilfsmethoden wie createEl() oder setText()
Listener ohne registerEvent Registrierungsmethoden des Plugins nutzen
feste Stile mit Farbwerten CSS-Variablen von Obsidian, damit Themes funktionieren
Beispielcode der Vorlage nicht entfernt ungenutzte Befehle und Klassen löschen
Wort „Obsidian“ im Plugin-Namen beschreibenden Namen ohne Produktnamen wählen
Node-APIs ohne Desktop-Kennzeichnung isDesktopOnly setzen oder Plattform prüfen

Die genauen Richtlinien veröffentlicht Obsidian in der Entwicklerdokumentation. Sie werden gelegentlich angepasst, deshalb lohnt vor jeder Einreichung ein aktueller Blick hinein. Allgemeine Hinweise zu Versionsnummern, Changelog und Pflege finden Sie im Bereich Add-ons veröffentlichen und pflegen.

Obsidian im Vergleich mit anderen Plugin-Plattformen

Wer mehrere Produktivitäts-Apps erweitert, erkennt Muster wieder. Auch Figma nutzt eine manifest.json und TypeScript, trennt aber Dokumentzugriff und Oberfläche in zwei Teile mit Nachrichtenaustausch. Diesen Aufbau beschreibt der Artikel Figma-Plugin erstellen. Obsidian ist im Vergleich offener: Ihr Code hat direkten Zugriff auf Oberfläche und Dateien. Diese Freiheit ist bequem, verlangt aber umso mehr Zurückhaltung bei Datenzugriffen, Netzwerkverbindungen und Änderungen an fremden Notizen.

faq

Fragen und Antworten

Brauche ich TypeScript, oder geht auch reines JavaScript?
Obsidian lädt am Ende nur eine main.js, reines JavaScript funktioniert also. Die offizielle Vorlage ist aber auf TypeScript ausgelegt, und die Typdefinitionen des Pakets obsidian zeigen Ihnen im Editor sofort, welche Methoden und Parameter es gibt. Für mehr als ein paar Zeilen lohnt sich TypeScript.
Läuft mein Plugin auch in der mobilen App?
Grundsätzlich ja, solange Sie keine Node.js- oder Electron-Funktionen verwenden, die es nur am Desktop gibt. Wer solche Funktionen braucht, setzt in der manifest.json isDesktopOnly auf true. Testen Sie mobile Versionen gesondert, denn Oberfläche und Bildschirmgröße unterscheiden sich deutlich.
Warum wird mein Plugin nach einer Änderung nicht neu geladen?
Obsidian lädt Plugin-Code nur beim Aktivieren. Nach einem Build schalten Sie das Plugin in den Einstellungen aus und wieder ein oder laden Obsidian neu. Viele Entwickler nutzen dafür ein Hilfs-Plugin, das Plugins bei Dateiänderungen automatisch neu lädt.
Darf ich Daten aus dem Tresor an einen Server senden?
Technisch ist das möglich, Obsidian erwartet aber Transparenz. Netzwerkzugriffe, Telemetrie und Konten müssen in der README offengelegt werden. Notizen sind oft sehr persönlich, deshalb sollte ein Plugin standardmäßig lokal arbeiten und Übertragungen nur nach ausdrücklicher Aktivierung durchführen.