スズナ

【Obsidian】Wrot+JS Engineで遊んだ覚書

目次

 サイドバーにつぶやきを置ける軽くて使いやすいプラグインWrot(https://community.obsidian.md/plugins/wrot)と、JavaScriptをコードブロックにして使えるようにするJS Engine(https://community.obsidian.md/plugins/js-engine)を組み合わせたら楽しかったのでその記録です。

目的

 目的はほんとに個人的なアレなんでどうでもいいんですけど、座りっぱなしは良くないから1時間に1回ぐらい運動しよう!→気分を上げる為に運動カウンターがほしい! という理由で作りました。
 べつに運動カウンターじゃなくても、お薬カウンターとか、水飲みカウンターとか、なんか任意のカウンターで良いと思います。後で少し話すけどカウンターとかじゃなくても多分いけます。

やっ(てもらっ)たこと

 JS Engine用にJavaScriptのコードブロックを作って、それをWrotに投稿すれば使えるようになります。
 わたしはJavaScriptがちょっとしかわかんないのでほぼChatGPTに任せました。

 保管庫/data/counter.mdというファイルを作って、そこのYAML(プロパティ)でカウントする形式にしてます。
 カウンター用ファイルに以下のYAMLを入力しておきます。

YAML
---
radio_taiso_total: 0
---

 それから、Wrotに以下のJS Engine用コードブロックを貼り付けて投稿します。

JavaScript
📻 ラジオ体操

~~~js-engine
// ========================================
// 📻 ラジオ体操カウンター
//
// 保存先:data/counter.md
// counter用のファイルに数を保存します。
// ========================================


// ----------------------------------------
// 保存先のファイル
// ----------------------------------------

// ここを変更すれば、好きなMarkdownファイルを
// データ保存先にできます。
const counterPath = "data/counter.md";


// ----------------------------------------
// ファイルを取得
// ----------------------------------------

const file =
    app.vault.getAbstractFileByPath(counterPath);

if (!file) {
    throw new Error(
        `${counterPath} が見つかりません。`
    );
}


// ----------------------------------------
// 現在のカウントを取得
// ----------------------------------------

// counter.mdを直接読み込みます。

async function getCount() {

    const content =
        await app.vault.read(file);

    const match =
        content.match(
            /^radio_taiso_total\s*:\s*(\d+)\s*$/m
        );

    if (!match) {
        return 0;
    }

    const count =
        Number(match[1]);

    return Number.isFinite(count)
        ? count
        : 0;
}


// ----------------------------------------
// 表示
// ----------------------------------------

const wrapper =
    document.createElement("div");

const countText =
    document.createElement("p");

const addButton =
    document.createElement("button");

const resetButton =
    document.createElement("button");


// 現在のカウントを表示

let currentCount =
    await getCount();

countText.textContent =
    `📻 ラジオ体操:${currentCount}回`;


// ----------------------------------------
// 「体操した!」ボタン
// ----------------------------------------

addButton.textContent =
    "体操した!";

addButton.addEventListener(
    "click",
    async () => {

        // 連打による二重処理を防ぐ
        if (addButton.disabled) {
            return;
        }

        addButton.disabled = true;
        resetButton.disabled = true;

        try {

            // counter.mdの現在値を読み込む
            const count =
                await getCount();

            // 1増やす
            const newCount =
                count + 1;

            // YAMLを書き換える
            await app.fileManager.processFrontMatter(
                file,
                (frontmatter) => {

                    frontmatter.radio_taiso_total =
                        newCount;
                }
            );

            // 画面も更新
            currentCount =
                newCount;

            countText.textContent =
                `📻 ラジオ体操:${currentCount}回`;

        } finally {

            addButton.disabled = false;
            resetButton.disabled = false;
        }
    }
);


// ----------------------------------------
// 「リセット」ボタン
// ----------------------------------------

resetButton.textContent =
    "リセット";

resetButton.addEventListener(
    "click",
    async () => {

        if (resetButton.disabled) {
            return;
        }

        addButton.disabled = true;
        resetButton.disabled = true;

        try {

            // YAMLの数字を0にする
            await app.fileManager.processFrontMatter(
                file,
                (frontmatter) => {

                    frontmatter.radio_taiso_total = 0;
                }
            );

            // 画面も0にする
            currentCount = 0;

            countText.textContent =
                `📻 ラジオ体操:0回`;

        } finally {

            addButton.disabled = false;
            resetButton.disabled = false;
        }
    }
);


// ----------------------------------------
// 画面に追加
// ----------------------------------------

wrapper.appendChild(countText);
wrapper.appendChild(addButton);
wrapper.appendChild(resetButton);

container.appendChild(wrapper);
~~~

 これで使えます。簡単!

 コメント多めで作っ(てもらっ)たので、別のカウンターで使いたい人はちょっと調整すればいけると思います。

可能性の話

 こういうの作ったよ~ってポストしたらWrotの開発者さんが反応くださいました。

うれしい

 コードブロックの描画をObsidian本体に任せているからこういうことができる、ということなので、JS Engineで使える範囲のJavaScriptでいろいろ応用できそうです。
 Wrotの固定機能を使って文字とボタンを置きっぱなしに出来るのが強い。「新しい専用プラグインを入れるほどじゃないけどちょっとだけこういうことがしたい」みたいなのが結構できそうな気がします。