Jazzと読書の日々

iPadを筆記具として使う方法を模索します

Obsidian:Excalidrawをmermaidに変換

じゃあ、これが「できる」ということか。

mermaid化

Excalidrawを非圧縮にするとCopilotで解析できる。

これはこれで面白いですが、そのたびgeminiと通信するのってなんか非効率です。 Templaterスクリプトにできないものですかねぇ。

ということでCopilotに助けてもらいました。

{activeNote}の内容をmermaidのflowchartに変換します。
以上のような作業を行うTemplaterスクリプトを考えてください。

excalidraw_to_mermaid.md

出てきたコードを少し手直しします。

コピペのとき改行コードが消えたのに気づかなくて、初めは動きませんでした。 コンソール出力もモバイルでは意味がないのでNoticeに置き換え。

<%*
// excalidraw(非圧縮)をmermaidに変換します。
function getExcalidrawData() {
  const file = tp.file.content
  const startString = '```json'
  const endString = '```\n%%'
  const startIndex = file.indexOf(startString)
  const endIndex = file.indexOf(endString)

  if (startIndex === -1 || endIndex === -1) {
    new Notice('JSONデータが見つかりませんでした。')
    return null
  }
  const jsonString = file.substring(startIndex + startString.length, endIndex).trim()
  try {
    return JSON.parse(jsonString)
  } catch (e) {
    new Notice('JSONデータの解析に失敗しました:', e)
    return null
  }
}

function generateMermaidFlowchart(excalidrawData) {
  if (!excalidrawData) {
    return 'Excalidrawデータがありません。'
  }
  let mermaidCode = 'graph LR\n'
  const elements = excalidrawData.elements
  const textElements = elements.filter(x => x.type === 'text' && !x.isDeleted)
  const arrowElements = elements.filter(x => x.type === 'arrow' && !x.isDeleted)
  const nodeMap = new Map()
  textElements.forEach(x => {
    mermaidCode += `${x.id}("${x.text}")\n`
    nodeMap.set(x.id, x)
  })
  arrowElements.forEach(x => {
    const startElementId = x.startBinding?.elementId
    const endElementId = x.endBinding?.elementId
    if (startElementId && endElementId && nodeMap.has(startElementId) && nodeMap.has(endElementId)) {
      mermaidCode += `${startElementId} --> ${endElementId}\n`
    }
  })
  return '```mermaid\n' + mermaidCode + '```'
}

const excalidrawData = await getExcalidrawData()
if (excalidrawData) {
  const s = await generateMermaidFlowchart(excalidrawData)
  navigator.clipboard.writeText(s)
  new Notice("クリップボードに保存しました。")
} else {
  new Notice("Excalidrawデータが見つかりませんでした。")
}
%>

これをCommanderでファイルメニューに登録します。

使い方

昨日のExcalidrawで試してみます。

ファイルメニューの「Open as Markdown」でMarkdownにします。 TemplaterスクリプトMarkdownファイルじゃないと実行できないからです。

「Templater: Insert excalidraw_to_mermaid.md」を実行。

クリップボードに保存しました」と出るので貼り付けると:

graph LR
LnU3qnBE("マチュ")
4SepEw7u("ニャアン")
hmyelOee("シュウジ")
rSjW7xS7("アンキー")
DfIS6dYK("ジェジー")
l7L4YUVt("ナブ")
hmyelOee --> LnU3qnBE
4SepEw7u --> LnU3qnBE
rSjW7xS7 --> hmyelOee
hmyelOee --> DfIS6dYK
DfIS6dYK --> hmyelOee
l7L4YUVt --> rSjW7xS7

ちゃんと仕上がってます。 左右反転してるけど。 LRRLにすればいいか。

アクションが組める

何より驚きがCopilotに「Templaterスクリプト」が伝わったこと。 このプロンプトでコード生成できるなら、今後作業を自動化したいとき、そのスクリプトをCopilotと考えていけるということです。

これは大きい。 Obsidianの魅力は自作アクションを組めることと思っているので、その手助けをしてもらえる。

自分のニーズにぴったりくるプラグインはありません。 プラグインは「その作者のニーズ」から生まれているからです。 自分と一致するわけではない。

そのとき自前でアクションが作れるのは重要なのですが、まあ、時間がかかる。 暇を見つけて取り組んでも、どこでエラーが出たのかわかりにくい。 それ専用の開発環境があるわけではありません。

でもCopilotに外注し、そのスクリプトを読んでカスタマイズするのは簡単です。 今回もJSON回りの処理に関しては、独力でできたかどうか怪しい。 理屈がわかれば「なるほど」なんだけど。

まとめ

原稿のためのマッピングをExcalidrawでする手もあるなあ。 キーワードを並べて、その関係性を考えて、書くための「地図」を作り上げる。