はじめに
みなさんこんにちは! Obsidian ユーザまもなく四年目のWです。
普段 Obsidian をデジタル手帳・人生データベースとして使っており、プログラマーとして、自分のニーズに合わせてプラグインも作っております。
今回は、Obsidian のプラグインをゼロから作る〜配布までの流れを紹介していきたいと思います。
0. そもそも Obsidian って何?
Obsidian とは、ローカルの Markdown ファイルをベースにしたナレッジマネジメントツールのことです。
単なる「メモアプリ」ではなく、エンジニア視点で見ると、かなりおもしろい特徴があります。
- すべてのデータはローカルの Markdownファイル(バージョン管理や、他のツールとの連携がしやすい)
- 使用者誰でも、プラグインを作って機能拡張できる (Markdownファイル専攻の VSCode みたいです)
つまり、Obsidian は機能を「買う」ツールではなく、「作り足せる」ツールです。
Obsidian の上限は、ほぼ「使う人の技術力」で決まる。
今回はその具体例として、次のようなシンプルなプラグインをゼロから作ってみます。
ステータスバーに現在の文字数 / 目標文字数(進捗バー付き)を表示するプラグイン
TypeScript を少し触ったことがあれば、難易度はそこまで高くありません。
一度この流れを経験しておくと、「あ、欲しい機能があれば自分でプラグイン書けばいいんだな」という感覚が掴めます。
1. 開発環境の準備
1.1 Node.js を用意する
Obsidian プラグインは TypeScript で書いて、Node.js でビルドできます。
(Node.jsのインストール方法はたくさん記事紹介があると思うため割愛)
以下ように、バージョンを確認します。
node -v
npm -v
Node 18 以上であればだいたい問題ありません。
1.2 公式サンプルプラグインをクローンする
一から構成を組むより、公式のサンプルから始めたほうが圧倒的に楽です。
git clone https://github.com/obsidianmd/obsidian-sample-plugin.git obsidian-wordcount-plugin
cd obsidian-wordcount-plugin
npm install
ディレクトリ構成はだいたいこんな感じです。
.
├─ src
| ├─── main.ts // プラグインのエントリポイント
| └─── settings.ts // プラグインのオプション設定
├─ manifest.json // プラグインのメタ情報
├─ package.json
├─ styles.css // スタイルシート、見た目調整用
├─ versions.json // プラグインのバージョン情報
└─ tsconfig.json
今回は主に以下3ファイルを使う:
-
manifest.json
-
main.ts
-
styles.css
補足:サンプルは簡単なやつであり、プログラムはmain.tsファイル一本にまとめます。 settings.ts は削除しておく。
2. manifest.json を設定する
manifest.json は、Obsidian に「このフォルダはこういうプラグインです」と認識させる定義ファイルです。
次のように、中身のメタ情報を書き換えていきます。
{
"id": "word-count-progress",
"name": "Word Count Progress",
"version": "1.0.0",
"minAppVersion": "1.5.0",
"description": "ステータスバーに現在の文字数と目標に対する進捗を表示するシンプルなプラグインです。",
"author": "Your Name",
"authorUrl": "",
"isDesktopOnly": false
}
ポイントだけ整理すると:
-
id:プラグインの一意 ID。英小文字+ハイフンあたりで付けておけば OK です
-
name:Obsidian の「コミュニティプラグイン」一覧で表示される名前
-
description:一行の説明文。プラグインの機能を簡潔に紹介
-
minAppVersion:対応する Obsidian の最小バージョン。手元のバージョンに合わせて設定
ここまでで、Obsidian 側からは「Word Count Progress というプラグインがある」と認識できる状態になります。
3. main.ts にプログラムを実装
今回のサンプルとして、以下処理を実装します。
-
プラグイン読み込み時にステータスバーに領域を作る
-
アクティブなファイルやエディタ内容が変わったら、文字数を再計算する
-
「現在の文字数 / 目標文字数(%表示+progress 要素)」を表示する
-
目標文字数は設定画面から変更できるようにする
コード全体はこんなイメージになります。
import {
App,
Plugin,
PluginManifest,
PluginSettingTab,
Setting,
} from "obsidian";
interface WordCountSettings {
targetWords: number;
}
const DEFAULT_SETTINGS: WordCountSettings = {
targetWords: 2000,
};
export default class WordCountPlugin extends Plugin {
settings: WordCountSettings;
statusBarItemEl: HTMLElement;
constructor(app: App, manifest: PluginManifest) {
super(app, manifest);
}
async onload() {
console.log("Loading WordCountPlugin");
// 設定を読み込む(なければデフォルト値)
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
// ステータスバーに表示用の要素を作成
this.statusBarItemEl = this.addStatusBarItem();
this.statusBarItemEl.setText("Words: 0");
// アクティブなノートが変わったとき
this.registerEvent(
this.app.workspace.on("active-leaf-change", () => {
this.updateStatusBar();
}),
);
// エディタの内容が変更されたとき
this.registerEvent(
this.app.workspace.on("editor-change", () => {
this.updateStatusBar();
}),
);
// 設定タブを追加
this.addSettingTab(new WordCountSettingTab(this.app, this));
// 初期表示
this.updateStatusBar();
}
onunload() {
console.log("Unloading WordCountPlugin");
}
// ステータスバーの表示を更新する
async updateStatusBar() {
const file = this.app.workspace.getActiveFile();
if (!file) {
this.statusBarItemEl.setText("No file");
return;
}
const content = await this.app.vault.read(file);
const words = this.countWords(content);
const target = this.settings.targetWords || DEFAULT_SETTINGS.targetWords;
const percent = target > 0 ? Math.min((words / target) * 100, 999) : 0;
const percentStr = percent.toFixed(1);
this.statusBarItemEl.empty();
const container = this.statusBarItemEl.createDiv({
cls: "wordcount-status",
});
container.createSpan({
text: `${words}/${target} (${percentStr}%) `,
cls: "wordcount-text",
});
const progress = container.createEl("progress", {
cls: "wordcount-progress",
});
progress.setAttribute("max", target.toString());
progress.setAttribute("value", Math.min(words, target).toString());
}
// 日英混在テキストの「ざっくり文字数」を数える
countWords(text: string): number {
if (!text) return 0;
// まずは Markdown 記法っぽいものをある程度削る
const cleaned = text
.replace(/```[\s\S]*?```/g, "") // コードブロック
.replace(/`{1,3}.*?`{1,3}/g, "") // インラインコード
.replace(/<!--[\s\S]*?-->/g, "") // コメント
.replace(/[#>*\-+`~_{}$!|]/g, "") // 記号類
.replace(/$begin:math:display$\\\[\(\.\*\?\)$end:math:display$\]/g, "$1") // [[wikilink]]
.replace(/$begin:math:display$\(\.\*\?\)$end:math:display$$begin:math:text$\(\.\*\?\)$end:math:text$/g, "$1"); // [text](link)
// 日本語など CJK は1文字ずつカウント
const cjkChars = cleaned.match(/[\u4e00-\u9fa5\u3040-\u30ff\u3400-\u4dbf\u31f0-\u31ff]/g) || [];
// 英数字は単語単位でカウント
const englishWords =
cleaned
.replace(/[\u4e00-\u9fa5\u3040-\u30ff\u3400-\u4dbf\u31f0-\u31ff]/g, " ")
.match(/\b\w+\b/g) || [];
return cjkChars.length + englishWords.length;
}
async saveSettings() {
await this.saveData(this.settings);
}
}
// 設定画面(目標文字数を変更できるようにする)
class WordCountSettingTab extends PluginSettingTab {
plugin: WordCountPlugin;
constructor(app: App, plugin: WordCountPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
containerEl.createEl("h2", {
text: "Word Count Progress 設定",
});
new Setting(containerEl)
.setName("目標文字数")
.setDesc("進捗計算に使う目標文字数(例:2000)。")
.addText((text) =>
text
.setPlaceholder("2000")
.setValue(this.plugin.settings.targetWords.toString())
.onChange(async (value) => {
const num = Number(value);
if (!Number.isNaN(num) && num > 0) {
this.plugin.settings.targetWords = num;
await this.plugin.saveSettings();
this.plugin.updateStatusBar();
}
}),
);
}
}
ざっくりポイントを整理すると:
-
onload()
プラグイン有効化時に呼ばれる。
-
設定ロード
-
ステータスバー要素の作成
-
イベントの登録
-
初回更新
をここでまとめて行う。
-
-
workspace.on("active-leaf-change") / "editor-change"
- アクティブノート変更・編集内容変更をフックして、文字数再計算をトリガー。
-
vault.read(file)
- 実際の Markdown テキストを取り出す API。
-
countWords()
- 文字数カウント関数。
今回のサンプルは、「実用上そこまでズレない程度」のシンプルな実装に留めている。
みなさんも自分ニーズに合わせて中身のregex(正規表現)マッチ処理を添削してみてください!
- 文字数カウント関数。
4. ステータスバーの見た目を少し整える(styles.css)
updateStatusBar() の中で
-
wordcount-status
-
wordcount-progress
-
wordcount-text
というクラス名を付けているので、styles.css で見た目を調整します。
.wordcount-status {
display: flex;
align-items: center;
gap: 4px;
font-size: 11px;
}
.wordcount-progress {
width: 120px;
height: 8px;
}
.wordcount-text {
white-space: nowrap;
}
ステータスバー自体の高さが低いので、情報量は控えめ&横にコンパクトに並べるくらいがちょうど良いです。
5. 手元の Obsidian で動かしてみる
プラグインの実装が完了で、実際に Obsidian 上で動かしてみます。
5.1 ビルド
プラグインプロジェクトのルートディレクトリでビルドを実行します。
npm run build
エラーが出なければ、ルートに main.js(+型定義ファイルなど)が生成されます。
5.2 Vault のプラグインフォルダにコピーする
Obsidian の plugins 配下に、今回のプラグインの格納フォルダを作ります。
例:
yourValut/.Obsidian/plugins/word-count-progress/
作ったフォルダに、以下4ファイルをコピーします。
-
manifest.json
-
main.js
-
styles.css
-
versions.json
5.3 Obsidian から有効化する
-
Obsidian を開き、設定 → 「コミュニティプラグイン」を開く
-
「コミュニティプラグインを有効にする」を ON にする
-
下の一覧から Word Count Progress を探して、有効化
任意のノートを開くと、ウィンドウ下部のステータスバーに
1234/2000 (61.7%)
のような表示が出ていれば成功です。
こんな感じです:
また、プラグインの設定画面より、目標文字数の変更も可能です。
こんな感じです:
6. みんなにも使ってもらうには?プラグインの配布方法
ここまでで、Obsidian プラグインの開発フローを一通り体験したいただいたと思います。
-
環境構築
-
プラグイン構造(manifest / main)
-
Obsidian API の基本(workspace, vault, status bar, settings)
-
ビルド&インストール
ただし...自分用に作ったプラグインがそこそこ便利だと、
「これ、チームメンバーにも配りたいな」
「できればコミュニティにも公開したいな」
という気持ちが出てきましたでしょうか?
以下、プラグインの配布についても紹介していきたいと思います。
6.1 チーム内・ローカル配布:フォルダをそのまま配る
一番お手軽なのは、プラグインフォルダごと共有する方法です。
-
word-count-progress ディレクトリを zip に固める
中身:manifest.json, main.js, styles.css, versions.json など
-
チームメンバーに配布して、各自の Vault に解凍してもらう
YourVault/.obsidian/plugins/word-count-progress/
-
あとは Obsidian の「コミュニティプラグイン」画面から有効化するだけ
チーム内利用 / 小範囲共有の場合、この方法でも十分です。バージョンアップ時は zip を差し替えてもらう運用でいけます。
6.2 GitHub で公開してドキュメントを整える
もう少しちゃんとやりたい場合は、GitHub で公開もできます。
-
GitHub に public リポジトリを作成
-
プラグインのソースコードを push
-
README に以下を明記
-
プラグインの概要
-
対応 Obsidian バージョン
-
インストール方法(手動コピー、もしくは Obsidian のサードパーティインストーラ経由など)
-
-
バージョンアップのたびに:
-
manifest.json / versions.json のバージョンを更新
-
Git タグや Release をしておく
-
こうすると、Issue / PR を通じて他のユーザーと一緒にプラグインを開発していくこともできますし、
内部ツールとして紹介するときにも、URL を1つ貼るだけで済むようになります。
6.3 Obsidian 公式コミュニティプラグインとして公開する
さらに一歩進めて、Obsidian アプリ内の検索からインストールできるようにしたい場合は、
公式の「Community Plugins」リポジトリに登録する必要があります。
流れとしては:
-
プラグインの GitHub リポジトリを公開状態にする
-
manifest.json と versions.json を、公式フォーマットに沿って整える
-
バージョン番号
-
ダウンロード URL
などを記載
-
-
Obsidian の公式プラグインインデックス用リポジトリに PR を出す
-
レビュー&マージされると、Obsidian アプリの「コミュニティプラグイン」一覧に表示される
ここまでやると、ユーザーは Obsidian 上でプラグイン名を検索して「インストール」ボタンを押すだけになります。
いわば「Obsidian のアプリストアに自作プラグインを並べる」イメージです!
おわりに
今回は、
-
Obsidian って IT エンジニアと相性いいな
-
TypeScript でシンプルなプラグインの開発手順
-
手元で動かすところから、他人に配るところまでの流れ
をひととおり紹介してみました。
一度でもプラグイン開発を経験すると、Obsidian は「与えられた機能を使うツール」から、「自分のワークフローに合わせて拡張していくプラットフォーム」に見え方が変わってきますね!
日々のドキュメント執筆や情報整理の中で
「ここがもう少しこうだったら便利なのにな」
と感じるポイントがあれば、その違和感こそが次のプラグインの種かもしれません。
以上です。
記事を最後までご覧いただき、ありがとうございます!お役に立てたら、「いいね!」をいただけますと嬉しいです :D

