辛宝Otto

辛宝Otto 的玄酒清谈

北漂前端程序员儿 / 探索新事物 / Web Worker 主播之一/内向话痨
xiaoyuzhou
email

速通 Obsidian Docs - プラグイン開発に重点を置く

image.png

内部関連:

  • [[【技術的視点からの xlog】よりスムーズな使用体験 3 Obsidian プラグインの実装]]

関連:

公式コンテンツは多い:

  • プラグイン開発、今回のコア
  • テーマ開発。今回は無視
  • 参照 API の列挙
  • その他

下にスクロールして、クイックコードスニペットを見てください

プラグイン開発#

私は、純粋な js のシンプルなプラグインを紹介することで、その構造と基本的な使い方を説明したいと思います。このプラグインは非常に便利で、私たちがより良く開発し、デバッグするのに役立ちます。皆さんが使いやすくするために、「hot-reload」という名前のプラグインを先にインストールすることをお勧めします。これは https://github.com/pjeby/hot-reload で見つけることができます。

vue を使用しているユーザーには、vue3、vite4、そして obsidian プラグインを組み合わせたテンプレートをお勧めします。これにより、開発をより簡単に始めることができます。このテンプレートは https://github.com/Otto-J/Obsidian-Vue-Starter で見つけることができます。

プラグインの具体的な使い方を説明する前に、プラグインのライフサイクルについて紹介したいと思います。プラグインのライフサイクルは onload と onunload の二つの段階を含みます。

モバイルプラグインは無視します。

vue で開発されたページは ItemView にマウントされます。これは、私たちが ItemView 内で vue のさまざまな機能を使用してカスタムページを開発できることを意味します。

  • 開始
    • 純粋な js のシンプルなプラグインを紹介し、構造と基本的な使い方を説明
    • 開発とデバッグを便利にするために、できるだけ早く https://github.com/pjeby/hot-reload をインストール
    • vue ユーザーはこのテンプレートを使用:vue3+vite4+obsidian plugin
    • ライフサイクル onload - onunload
    • モバイルプラグイン、シミュレーションを開始する方法を提供、知っておけば良い
    • vue で開発されたページは ItemView にマウントされる
  • UI
    • UI 構造の紹介、異なる領域の呼称を整合
      • 異なる位置を紹介する画像を出す、sidebar は ribbon actions と呼ばれ、少し奇妙
      • これは画像Pasted image 20230909121152
    • 【コマンドパレット】
      • vsCode のように cmd + p を使用してコマンドパレットを呼び出すことができ、プラグイン開発は onload で this.addCommand メソッドを呼び出して登録します。複雑ではありません。例えば、現在アクティブなエディタの内容を処理することができます。
      • コマンドには前置きの判断を追加でき、checkCallback メソッドを使用して、実行されるかどうかを事前に検証します。すべてに追加して、安心です。
      • エディタにアクセスでき、editorCallback メソッドがあり、パラメータには edtor と view の二つのパラメータがあります。もちろん editorCheckCallback オプションもあります。
      • ホットキーを登録でき、トリガー方法を追加します。詳細は無視します。
    • 【右クリックメニュー】
      • obsidian.Menu をインポートし、menu.addItem をインスタンス化できます。これは sidebar に配置されます。
      • また、ファイルリスト file-menu とエディタの右上角に配置することもできますPasted image 20230909125832
    • 【html コンテナ】プラグイン設定に html コンテナを配置できます。PluginSettingTab 内で
      • 伝統的な js createEl を使用できます。h レンダリング関数に似ています。
      • プラグインのルートディレクトリの styles.css、vue を使って手間を省きます。
    • 【アイコン】ここから探します、https://lucide.dev/ flutter をサポートしていて良いです。
      • カスタムアイコンは svg の元の内容を準備する必要があります。vue icons の一式を気にする必要はありません。
    • 【モーダル】ポップアップウィンドウでユーザーに情報を入力させます。
      • Obsidian.Modal からインターフェースをインポートし、onOpen と onClose メソッドを実装する必要があります。
      • クリックして送信する際に対応するコールバックがあります。
      • SuggestModal という補助入力があり、FuzzySuggestModal は無視します。
    • 【sidebar】公式の呼称は ribbon actions です。
      • 使用法は、デモで必ず言及される this.addRibbonIcon です。
    • 【settings】公開設定、プラグインの設定を記入する場所です。
      • プラグインの設定は永続化されるため、プロセスはまず this.loadData を読み込み、設定結果をマージします。
      • 公式のインスタンスのコードはドキュメントと同じです。
    • 【ステータスバー】右下のステータスバーです。
      • this.addStatusBarItem を追加します。
      • 複数追加できます。
    • 【views】リソース管理、エディタ、プレビューはすべて view です。
      • view ID を取得する getViewType。
    • 【workspace】理解できません、ツリー構造です。
      • 複数の文書を並行して開くことができ、分割 split の方法です。
      • 理解できません、文書がひどいです。
      • キーワードは Leaf なんとかです。
      • 下のショートカット操作のコードスニペットを直接見てください。
  • Editor
    • 【Editor】クラスを取得し、編集モードの MD。
      • 下のコードを見てください。
    • 【Markdown 後処理】プレビュー モードの md は html であり、html を調整できます。
      • 例 1、 を絵文字アイコンに置き換えます。
      • 例 2、特殊な構文の内容を解析し、追加します。つまり、データ変換です。例えば csv をテーブルに変換します。
    • 【エディタ拡張】最初は見なくても良いです。CodeMirror6 の内容を紹介します。
      • 基盤は codeMirror6 に依存しており、拡張を書くことができます。ここでは見ません。
      • 【装飾】上記のエディタ拡張の内容にスタイルを追加しますが、関係ありません。
        • 最初は見ません。
      • 【State fields】エディタ拡張のもので、理解できません。
      • 【State management】エディタ拡張の状態管理。
        • 取り消しを行う場合、状態を保持する必要があります。
      • 【View Plugins】エディタ拡張プラグインで、viewport にアクセスできますが、無視します。
      • 【viewport】高性能のエディタには仮想スクロールが欠かせず、カクつきを避け、文書をスクロールする際に viewport が更新され再計算されます。
      • 【エディタ通信】無視します。
  • Releasing 公開
    • CI 操作 - ドキュメントはプラグインの構築を支援するための Github アクションを提供しています。
    • 公式マーケットに提出する前に、Readme.md ライセンス、mainfest.json 公式の例があります。
    • ドキュメントに従って操作すれば良いです。公式は json を維持しており、pr を提出すれば良いです。
    • node と electron はデスクトップでのみ使用を許可します。
    • beta バージョンの場合、brat プラグインを考慮できます。
  • Event
    • 私たちはタイマーが必要かもしれません。例えば setTimeInterval を使用して何かを更新するために、ここには専用の this.registerInterval メソッドがあります this.registerInterval( window.setInterval(() => this.updateStatusBar(), 1000) );
    • 時間のフォーマット、内蔵の obsidian.moment。
  • Valut
    • ノートの集合、これらの fs 操作をカプセル化しています。
    • ドキュメントは読み取り専用で操作せず、cachedRead () を使用します。
    • ドキュメントを読み取り、変更するには read () を使用します。
    • ファイルを変更するには vault.modify () を使用します。これは上書き操作です。
    • valut.process コールバックアドレスには data があり、現在の内容を示し、同期操作のみをサポートします。
    • 非同期変更は vault.cachedRead () - 非同期操作 - vault.process () 更新を考慮します。
    • ファイルを削除する trash () はゴミ箱であり、完全に削除するには delete () を使用します。
    • app.vault.getAbstractFileByPath("folderOrFile") 絶対パスの結果はファイルまたはフォルダであり、instanceof TFile/ TFolder で判断します。

よく使うスニペット#

ファイルの内容を読み取るには二つの方法があります:read()cachedRead()。

  • ユーザーに内容を表示するだけの場合、ディスクからファイルを何度も読み取るのを避けるために使用します。cachedRead()
  • 内容を読み取り、変更し、ディスクに書き戻す場合は、可能性のある古いコピーでファイルを上書きしないように read() を使用します。
// アクティブなテキストの内容を取得
app.workspace.getActiveFileView().data

// も可能
app.workspace.activeEditor

// ユーザーがアクティブにしたカーソル位置
app.workspace.getActiveFileView().editor.getCursor()
// {line:2,ch:2}

// 指定位置にテキストを追加、例えば現在の日付など、またはテンプレート
app.workspace.getActiveFileView().editor.replaceRange('222',{line: 2, ch: 2})

// 現在アクティブなエディタ
const editor = app.workspace.getActiveFileView().editor

// ユーザーが選択した部分の内容
app.workspace.getActiveFileView().editor.getSelection()

// 内容を置き換え、例えばアップロード後に cdn アドレス sha に変わる
editor.replaceSelection('xxx')

// すべての md 文書を取得
app.vault.getMarkdownFiles()

// すべてのファイルを取得し、その後フィルタリング
app.vault.getFile()

// テキスト内容を取得
var file = app.vault.getMarkdownFiles()[0]
const content = await app.vault.cachedRead(file)

// 内容を変更、これは同期操作です
app.vault.process(file, (data) => { return data.replace(":)", "🙂"); })


this.app.workspace.on('editor-paste', (evt, editor, view) => {
if (evt.clipboardData) {}
}))

// 現在のファイル file を取得することができます
this.app.workspace.on("file-menu", (menu, file) => {
if (file instanceof TFile) {}
});

// 文字列パスを指定し、ファイルが存在するかを判断します。より安全です
const isExist = await this.app.vault.adapter.exists('xx.md')

// 文字列を指定し、最適な一致ファイルを探します
app.metadataCache.getFirstLinkpathDest('フォルダ/ダウンロード.png','')

// TFile を取得し、内容を読み取ります
app.metadataCache.getFileCache(file)
// その中の embeds は関連する画像を示しますが、外部リンクの標準構文は含まれていません。組み合わせて使用する必要があります。
// frontmatter はその中の fm
// frontmatterPosition は fm の開始位置と終了位置で、削除して置き換えることができます
// links は現在の記事の外部リンクを示します


// 画像のバイナリ内容を読み取り、後で blob を表示し、アップロードします
const conArrayBuffer = await plugin.app.vault.readBinary(TFile);
// バイナリに変換し、post でアップロードします
const blob = new Blob([conArrayBuffer], {
    type: "image/" + obInnerFile.extension,
});



front-matter の操作は比較的一般的で面倒です。一般的なソリューションを提供します。

export const useObsidianFrontmatter = (file: TFile, app: App) => {
  // より意味のある関数名を使用
  const doesFileExist = () => !!app.metadataCache.getFileCache(file);

  const currentFrontMatter = () =>
    app.metadataCache.getFileCache(file)?.frontmatter ?? {};
  const addOrUpdateFrontMatter = async (obj: Record<string, string>) => {
    const fileCache = app.metadataCache.getFileCache(file);
    // ファイルが存在しない場合、直接戻ります
    if (!fileCache) {
      new Notice("ファイルが存在しません");
      return;
    }

    const currentFrontMatter = fileCache?.frontmatter ?? {};
    const newFrontMatter = `---\n${stringifyYaml({
      ...currentFrontMatter,
      ...obj,
    })}\n---\n`;

    // const { frontmatterPosition } = fileCache;
    // readcontent or con = vault.cachedRead(file)
    const fileContents = await app.vault.read(file);

    const frontmatterPosition = fileCache.frontmatterPosition ?? {
      start: {
        line: 0,
        col: 0,
        offset: 0,
      },
      end: {
        line: 0,
        col: 0,
        offset: 0,
      },
    };

    // ここでのロジックは少し複雑ですが、ファイル内容を再書き込み、後で API があれば一行のコードで解決できるかもしれません。metadataCache.update のように。
    const {
      start: { offset: deleteFrom },
      end: { offset: deleteTo },
    } = frontmatterPosition;

    const newFileContents =
      fileContents.slice(0, deleteFrom) +
      newFrontMatter +
      fileContents.slice(deleteTo);

    await app.vault.modify(file, newFileContents);
  };

  return {
    doesFileExist,
    addOrUpdateFrontMatter,
    currentFrontMatter,
  };
};

ts api 迅速な理解#

公式文書は特に悪く書かれており、周辺でも良い紹介は見当たりません。

迅速に列挙します。

  • abstract-input-suggest 入力ボックスにいくつかのヒントを提供します。例えば、過去のタグなど。
  • abstract-text-component テキスト入力ボックス関連の抽象クラスのようです。
  • app アプリケーションレベルのプロパティで、ファイル、vault、ホットキー、ワークスペースなどを取得できます。
  • base-component 無効に設定できます。
  • class BlockCache extends CacheItem
  • block-sub-path-result
  • button-component ボタンコンポーネント、見なくても良いです。統一してカプセル化されています。
  • cached-metadata--interface
  • cache-item--interface
  • closeable-component--class 閉じるメソッドを提供します。見なくても良いです。すべてカプセル化されています。
  • ColorComponent--class、カラーセレクターで、色の hex を取得でき、カプセル化できます。
  • Commond--interface コマンド関連のメソッドを登録します。
  • component--class コアエントリコンポーネントです。
  • data-adapter--interface、ファイルと温酒を処理し、Vault の API を推奨します。
  • data-write-options--interface
  • debouncer--interface
  • dropdown-component--class カプセル化できます。
  • Editor--class、codemirror のバージョン差異を平準化するインターフェースで、多くの補助機能を提供できるはずです。
  • editor-change--interface
  • editor-position--interface
  • editor-range--interface
  • editor-range-or-caret--interface
  • editor-scroll-info
  • editorxxx 略過
  • events--class イベント関連です。
  • extra-button-component--class カプセル化されています。
  • file-manager--class UI の観点からファイルの作成、削除、名前変更を管理します。
  • file-stats--interface
  • file-system-adapter implements DataAdapter--class
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。