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?

GitBucket Markdown Enhanced Plugin に WaveDrom サポートを追加しました

Last updated at Posted at 2025-12-07

概要

GitBucket用のプラグイン GitBucket Markdown Enhanced Pluginを開発しています。

今回は、Visual Studio Code のプラグイン Markdown Preview Enhancedがサポートしている WaveDrom のサポートを追加しました。

前回の記事

必要なスクリプトを同梱

src\main\resources\gme\assets\wavedrom ディレクトリを作成し、default.jswavedrom.min.js を同梱しました。

必要なスクリプトを読み込む修正

src\main\scala\Plugin.scalajavaScripts メソッドを修正しました。

diff --git a/src/main/scala/Plugin.scala b/src/main/scala/Plugin.scala
index a221329..f29afc7 100644
--- a/src/main/scala/Plugin.scala
+++ b/src/main/scala/Plugin.scala
@@ -88,13 +88,17 @@ class Plugin extends gitbucket.core.plugin.Plugin {
       |<link rel='stylesheet' href='$jsPath/admonition.css'>
       |<link rel='stylesheet' href='$jsPath/katex.min.css'>
       |<link rel='stylesheet' href='$jsPath/gme.css'>
-      |<script src="${jsPath}/admonition.js">
+      |<script src="${jsPath}/admonition.js" type="text/javascript">
       |</script>
-      |<script src="${jsPath}/katex.min.js">
+      |<script src="${jsPath}/katex.min.js" type="text/javascript">
       |</script>
-      |<script src="${jsPath}/mermaid.min.js">
+      |<script src="${jsPath}/mermaid.min.js" type="text/javascript">
       |</script>
-      |<script src="${jsPath}/gme.js">
+      |<script src="${jsPath}/wavedrom/default.js" type="text/javascript">
+      |</script>
+      |<script src="${jsPath}/wavedrom/wavedrom.min.js" type="text/javascript">
+      |</script>
+      |<script src="${jsPath}/gme.js" type="text/javascript">
       |""".stripMargin)
   }

関係ない修正が入ってしまいましたが、肝となるのは次の行です。

      |<script src="${jsPath}/wavedrom/default.js" type="text/javascript">
      |</script>
      |<script src="${jsPath}/wavedrom/wavedrom.min.js" type="text/javascript">
      |</script>

WaveDrom のコードブロックを生成できるように既存の処理を修正

src\main\scala\io\github\yasumichi\gme\MarkdownEnhancedNodeRenderer.scalarenderFencedCodeBlock メソッド(旧名 render メソッド) を修正し、新しく作成した renderWaveDrom メソッドを呼び出すようにしました。

修正後の renderFencedCodeBlock メソッドは、次のコードになりました。

  private def renderFencedCodeBlock(
      node: FencedCodeBlock,
      context: NodeRendererContext,
      html: HtmlWriter
  ): Unit = {
    val htmlOptions: HtmlRendererOptions = context.getHtmlOptions()
    val language: BasedSequence =
      node.getInfoDelimitedByAny(htmlOptions.languageDelimiterSet)

    if (language.equals("plantuml")) {
      renderPlantUML(html, node)
    } else if (language.equals("wavedrom")) {
      renderWaveDrom(html, node)
    } else {
      context.delegateRender()
    }
  }

上記で呼び出される renderWaveDrom メソッドは、次のコードです。

  private def renderWaveDrom(html: HtmlWriter, node: FencedCodeBlock): Unit = {
    var text = ""
    var seqs = node.getContentLines().toArray()
    for (i <- 0 to seqs.length - 1) text = text + seqs(i).toString()

    logger.info(text)
    html
      .withAttr()
      .attr("type", "WaveDrom")
      .tag("script")
    html.append(text)
    html.tag("/script")
  }

この段階で以下のようなマークダウンを書くと HTML への変換時に <script> ブロックが生成されるようになりました。


```wavedrom
{ signal : [
  { name: "clk",  wave: "p......" },
  { name: "bus",  wave: "x.34.5x",   data: "head body tail" },
  { name: "wire", wave: "0.1..0." },
]}
```

<script type="WaveDrom" id="InputJSON_0">{ signal : [
{ name: "clk",  wave: "p......" },
{ name: "bus",  wave: "x.34.5x",   data: "head body tail" },
{ name: "wire", wave: "0.1..0." },
]}
</script>

WaveDrom.ProcessAll() を実行しても図形が描画されない…

この段階で開発者ツールを使用して、WaveDrom.ProcessAll() を実行しましたが、以下のようなエラーメッセージが表示され、図形を描画することができませんでした。

Uncaught TypeError: points.item(...).type.toLowerCase is not a function
    exports https://cdnjs.cloudflare.com/ajax/libs/wavedrom/3.1.0/wavedrom.min.js:2
    onload 省略
wavedrom.min.js:2:17034
    exports https://cdnjs.cloudflare.com/ajax/libs/wavedrom/3.1.0/wavedrom.min.js:2
    onload 省略

調査したところ、SVG 要素が混在している場合に当該エラーが発生することが分かりました。

WaveDrom の lib\process-all.js を以下のように修正して、ビルドしたバージョンを使用することにしました。

diff --git a/lib/process-all.js b/lib/process-all.js
index 912847f..0f90c39 100644
--- a/lib/process-all.js
+++ b/lib/process-all.js
@@ -9,7 +9,7 @@ function processAll () {
     let index = 0; // actual number of valid anchor
     const points = document.querySelectorAll('*');
     for (let i = 0; i < points.length; i++) {
-        if (points.item(i).type && points.item(i).type.toLowerCase() === 'wavedrom') {
+        if (points.item(i).type && typeof(points.item(i).type) == "string" && points.item(i).type.toLowerCase() === 'wavedrom') {
             points.item(i).setAttribute('id', 'InputJSON_' + index);

             const node0 = document.createElement('div');

問題になったノードを調べたところ、type プロパティが文字列ではなく SVGAnimatedEnumeration { baseVal: 1, animVal: 1 } になっていたため、上記のように修正しました。

本件、本家に issue と Pull Request を送りました。

WaveDrom.ProcessAll() fails when SVG elements are mixed. · Issue #442 · wavedrom/wavedrom

Excludes SVG elements by yasumichi · Pull Request #441 · wavedrom/wavedrom

Reject されないと良いのですが…

WaveDrom.ProcessAll() を自動で呼ぶように修正

src\main\resources\gme\assets\gme.js を修正しました。

以下、抜粋です。

    document.addEventListener("DOMContentLoaded", function(){
        var preview = document.querySelector("#preview");
        if (preview) {
            const config = { attributes: false, childList: true, subtree: false };

            const observer = new MutationObserver((mutations) => {
                mutations.forEach(async (mutation) => {
                    if (mutation.addedNodes.length == 1) {
                        observer.disconnect();
                        renderKatex();
                        await mermaid.run();
                        WaveDrom.ProcessAll();
                        observer.observe(preview, config);
                    }
                });
            });

            observer.observe(preview, config);
        }
        renderKatex();
        WaveDrom.ProcessAll();
    });

以上で GitBucket で WaveDrom の図が表示できるようになりました。

スクリーンショットを貼ります。

image.png

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?