0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ObsidianでCalloutをしやすくするプラグインを作った

0
Posted at

こんにちは🌤 駆け出しフルスタックエンジニアのここあです!

普段はWezTermとObsidianを行き来している生活をしております。

今回は「コールアウトを右クリック一発で挿入できる」Obsidianプラグイン「Easy Callout」を作ったので紹介します。

そもそもコールアウトとは?という方や、Obsidianでコールアウトをよく使う方、ネストや折りたたみの書式をいつも手打ちしている方にぜひ読んでいただきたい内容です!


そもそもCalloutとは

ObsidianのCalloutとは、ノート内に注意書きや補足情報をブロック表示できる機能です。マークダウンでは以下のように書きます。

> [!note]- タイトル
> 本文の内容

レンダリングすると視覚的に強調されたブロックとして表示されます。- で折りたたみ、+ で展開固定、何もつけなければデフォルト表示になります。

利用できる種類は以下のとおりです。

種類 代表的な用途
note 一般的なメモ
tip / hint ヒント・コツ
warning / caution 注意
danger / error 危険・エラー
info / important 情報
success / check 成功・完了
question / faq 疑問・よくある質問
abstract / summary / tldr 要約
example
quote / cite 引用

私が感じた「課題」

エラーログやコマンド、Pythonスクリプトのような複数行のコードをメモするときにCalloutを多用してました。

「このエラーが出たらこのコマンドで直す」みたいな手順を折りたたみCalloutにまとめておくと、ノートがすっきりして後から読み返しやすくなります。


エラーログの例

ところが、これが想像以上に手間でした。Calloutの書式は 行頭に > を付ける必要があるため、コードが10行・20行になると…

> [!danger]- エラーログ
> Traceback (most recent call last):
>   File "main.py", line 42, in <module>
>     result = process(data)
>   File "main.py", line 18, in process
>     return transform(x)
>   File "main.py", line 9, in transform
>     raise ValueError(f"unexpected value: {x}")
> ValueError: unexpected value: None

全行に > を手打ちするか、書いてから全行を選択して > を付け直すか…どちらにしても面倒です。書式ミスで1行だけCalloutから外れてしまうことも何度かありました。

「選択してコールアウトにラップするだけにしてほしい」

具体的に足りていないと感じた機能は次の3つです。

  • 選択範囲のラップ(複数行のコードやエラーログをそのままCalloutの本文にしたい)
  • どこからでも呼び出せるCallout挿入UI(コードブロック内外を問わず使いたい)
  • ネストの自動判定(今どのCalloutの中にいるか勝手に数えてほしい)

これらを解決するために作ったのが「Easy Callout」です。


Easy Callout:できること

① どこからでもCalloutを挿入

右クリックメニューまたはコマンドパレット(Ctrl+P)から「コールアウトを挿入」を実行するとモーダルが開きます。コードブロックの中でも外でも、どこでも呼び出せます。

モーダルで入力する項目はこちらです。

入力項目 説明
マーク(種類) 公式13種 + エイリアス14種 + カスタムタイプからドロップダウンで選択
折りたたみ 展開(+)/ 折りたたみ(-)/ 指定なし の3択
タイトル Calloutのタイトル(省略可)
本文 Calloutの本文(複数行対応)

キーボード操作も対応しています。

キー 動作
Enter 本文フィールドで改行
Shift+Enter Calloutを挿入して閉じる
Esc キャンセルして閉じる

② ネストの自動判定

既存のCallout内にカーソルがある状態で実行すると、現在の深さを自動で検出して1段深いCalloutとして挿入します。

例えば、次のような親Calloutの本文行にカーソルを置いた状態で実行すると…

> [!info]- 親のCallout
> ここにカーソル ← 深さ1

自動的にこのように記載してくれます。

> [!note]- kaggleコンペ
> # Overview
>
> > [!todo]+ 提出期日
> > こちらに提出期日を書きます。
> # Rules
>
> > [!warning] 注意点
> > ここにはやってはいけないことを書きます。

自動的に >> [!tip]- タイトル 形式で挿入されます。3段以上のネストも同様に動作します。

モーダルを開いたときに「現在の位置は深さ N のコールアウト内です」と表示されるので、どの深さに挿入されるか一目でわかります。

③ 選択範囲のラップ

テキストを選択した状態で実行すると、選択した文字列がモーダルの本文フィールドに自動で読み込まれます。そのまま「挿入」を押すと、選択範囲がまるごと新しいCalloutに置換されます。

選択行が既に > で始まっている場合は、その最小深度を踏まえてネストされます。


設定

設定 → コミュニティプラグイン → Easy Callout から以下を変更できます。

設定項目 説明
デフォルトのタイプ モーダルを開いたときに初期選択されるCallout種別
デフォルトの折りたたみ 展開(+)/ 折りたたみ(-)/ 指定なしの既定値
カスタムタイプを追加 CSSスニペット等で独自定義したタイプ名を選択肢に追加

カスタムタイプ名は英小文字・数字・ハイフン・アンダースコアのみ使用できます。登録済みのカスタムタイプは設定画面で一覧表示され、ゴミ箱アイコンから削除できます。


インストール方法

手動インストール

  1. GitHubリリースページ から最新版をダウンロード

  1. Vault の .obsidian/plugins/easy-callout/ フォルダを作成

  2. main.jsmanifest.jsonstyles.css を配置

  3. Obsidian を再起動 → 設定 → コミュニティプラグイン → 有効化

コミュニティプラグインから(リリース予定)

今しばらくお待ちくださいませ(+_+)


使い方

Calloutを挿入する

右クリックから

  1. エディタの任意の位置にカーソルを置く(またはテキストを選択する)
  2. 右クリック → 「コールアウトを挿入」
  3. マーク・折りたたみ・タイトル・本文を入力
  4. Shift+Enter(または「挿入」ボタン)で完了

コマンドパレットから

Ctrl+P → 「コールアウトを挿入」を選択すると同じモーダルが開きます。

選択テキストをCalloutにラップする

  1. ラップしたいテキストを選択
  2. 右クリック → 「コールアウトを挿入」
  3. 本文フィールドに選択テキストが自動入力された状態でモーダルが開く
  4. 種別・タイトルを入力して「挿入」→ 選択範囲がCalloutに置換される


おまけ

Basesやノートの集約に

自分の活用方法ですが、Vault内にBasesが複数あるので、Callout内に入れることで、それを見やすくしています。
あくまでBasesファイルをそのまま表示するのではなくて、Calloutを用いて集約用のノートを作るイメージです。

他にもいい活用方法があればコメントやXなどで教えてください!

技術的なメモ

ネスト深度の自動検出

カーソル行(または選択範囲)の先頭にある > の数をカウントして深度を算出しています。選択範囲がある場合は各行の深度の最小値を採用します。空行はスキップします。

function countLineCalloutDepth(line: string): number {
  const match = line.match(/^((?:>\s?)+)/);
  if (!match) return 0;
  let depth = 0;
  for (const ch of match[1]) {
    if (ch === ">") depth += 1;
  }
  return depth;
}

Calloutスニペットの生成

深度に応じて "> ".repeat(depth + 1) でプレフィックスを生成します。折りたたみ状態は foldMarker として + / - / "" に変換してヘッダーに付加します。

const prefix = "> ".repeat(formData.depth + 1);
const foldMarker = formData.fold === "collapse" ? "-"
  : formData.fold === "expand" ? "+" : "";
const header = normalizedTitle
  ? `${prefix}[!${formData.type}]${foldMarker} ${normalizedTitle}`
  : `${prefix}[!${formData.type}]${foldMarker}`;

コードブロック内への挿入位置

コードブロック内でCalloutを挿入する場合は、カーソルの次の行を基本の挿入位置とし、ブロックの開始行〜終了行の間に収まるよう Math.min / Math.max でクランプしています。コードブロックの外にCalloutがはみ出さないようにするための工夫です。

const insertLine = Math.min(
  Math.max(cursor.line + 1, fencedBlock.startLine + 1),
  fencedBlock.endLine,
);

まとめ

課題 解決
Calloutの書式を毎回手打ちするのが面倒 右クリック / コマンドパレットからモーダルUIで挿入
折りたたみ記号(+ / -)を毎回考えるのが煩わしい モーダルのドロップダウンで3択から選ぶだけ
ネストの > の数を手で数えるのがつらい カーソル位置の深さを自動検出して適切にネスト
選択テキストをCalloutに囲むのが手間 選択したまま実行すれば本文が自動入力され、確定で置換
独自定義したCalloutタイプを毎回手入力している 設定タブでカスタムタイプを登録してドロップダウンに追加

GitHubでオープンソースとして公開していますので、バグ報告や機能要望はIssuesでお待ちしています。


リンク


おわりに

このプラグインはclaude様とペアプロをして作成しました。
毎度毎度ありがとうございます。☕

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?