krokiが使用出来ない時に自前でbytefieldを変換する

# 目的

krokiが動かない時に 自前で bytefields の画像を生成する

# 事前準備

nodejsをインストールする

fedoraの場合は
```
sudo yum install -y nodejs
```

# 手順

bytefield-svgを取ってきて webサーバとかの `node_modules` をインストールする
ちなみに `bytefield-svg` はリポジトリ内に `lib.js`が無いので結局インストールしないといけない

```shell
git clone --depth=1 https://github.com/Deep-Symmetry/bytefield-svg.git
cd bytefield-svg
npm install bytefield-svg express
```

## プロジェクトルートの `cli.js` を修正する

```diff
diff --git a/cli.js b/cli.js
index 6c98f95..18c7d04 100755
--- a/cli.js
+++ b/cli.js
@@ -3,7 +3,7 @@
 // A simple command line interface for rendering diagrams from standard input or a file,
 // to standard output or a file.
 
-const generate = require('./lib.js'); // The bytefield-svg DSL interpreter and diagram generator
+const generate = require('bytefield-svg/lib.js'); // The bytefield-svg DSL interpreter and diagram generator
 const fs = require('fs'); // Used to read and write files if needed.
 const commandLineArgs = require('command-line-args');
 const commandLineUsage = require('command-line-usage');
@@ -66,4 +66,4 @@ if (options.help) {
   } else { // Write to standard out.
     process.stdout.write(diagram);
   }
-}
\ No newline at end of file
+}
```

## Webサーバを立てる

server.js
```js

const express = require('express');
const { spawn } = require('child_process');
const path = require('path');
const app = express();
const port = 3000;

app.use(express.static('public'));
app.use(express.json());

app.post('/render', (req, res) => {
    const input = req.body.source || '';

    const cliPath = 'cli.js';

  let output = '';
  let error = '';

  const proc = spawn('node', [cliPath], { stdio: ['pipe', 'pipe', 'pipe'] });

  proc.stdin.write(input);
  proc.stdin.end();

  proc.stdout.on('data', data => output += data.toString());
  proc.stderr.on('data', data => error += data.toString());

  proc.on('close', code => {
    if (code === 0) {
      res.type('image/svg+xml').send(output);
    } else {
      res.status(500).send(`Error: ${error}`);
    }
  });

});

app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});

```

## htmlを作成する

public/index.html

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Bytefield Live Editor</title>
  <style>
    body { display: flex; height: 100vh; margin: 0; font-family: sans-serif; }
    textarea { width: 50%; padding: 1rem; font-family: monospace; }
    iframe { flex: 1; border: none; }
  </style>
</head>
<body>
  <textarea id="input" placeholder="# Enter bytefield here..."></textarea>
  <div id="preview"></div>
  <script>
    const input = document.getElementById('input');
    const preview = document.getElementById('preview');

    async function render() {
      const res = await fetch('/render', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ source: document.getElementById('input').value })
      });
        const svg = await res.text();
        const svg_data = svg.replace(/^<\?xml[^>]*\?>/, '');
        preview.innerHTML = svg_data;
    }

    input.addEventListener('input', () => {
      clearTimeout(input._timer);
      input._timer = setTimeout(render, 500); // debounce
    });
  </script>
</body>
</html>
```

## package.jsonにコマンドを作成する

`scripts` 内に `"server": "node server.js"` を追加する
それ以外は `npm install` したら勝手に追加される

```diff
diff --git a/package.json b/package.json
index 1ba4ac9..3e88437 100644
--- a/package.json
+++ b/package.json
@@ -5,13 +5,15 @@
   "main": "lib.js",
   "bin": "cli.js",
   "dependencies": {
+    "bytefield-svg": "^1.11.0",
     "command-line-args": "^5.1.1",
-    "command-line-usage": "^6.1.3"
+    "command-line-usage": "^6.1.3",
+    "express": "^5.1.0"
   },
   "devDependencies": {
     "@antora/cli": "3.1.2",
-    "@antora/site-generator": "3.1.2",
     "@antora/lunr-extension": "^1.0.0-alpha.8",
+    "@antora/site-generator": "3.1.2",
     "@djencks/asciidoctor-mathjax": "0.0.8",
     "shadow-cljs": "^2.21.0",
     "source-map-support": "^0.5.21"
@@ -20,7 +22,8 @@
     "build": "shadow-cljs compile lib",
     "release": "shadow-cljs release lib",
     "local-docs": "npx antora --fetch doc/local.yml",
-    "test": "./cli.js test.edn >test.svg"
+    "test": "./cli.js test.edn >test.svg",
+    "server": "node server.js"
   },
   "repository": {
     "type": "git",

```

## 実行

```
npm run server
```

## 編集と確認

ブラウザで `http://localhost:3000/` にアクセスして
画像を生成する

こんな感じ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786381/340f48f0-64bc-4383-aec4-2792059ffd2a.png)

# 全部入り

dockerでやるのが一番楽なことに気づいた

```shell
sudo yum install -y docker
sudo systemctl start docker
sudo docker run -p8000:8000 yuzutech/kroki
```

例えば以下のようなファイル(example.bf)を用意して

<details><summary>example.bf</summary>


```lisp
(defattrs :bg-green {:fill "#a0ffa0"})
(defattrs :bg-yellow {:fill "#ffffa0"})
(defattrs :bg-pink {:fill "#ffb0a0"})
(defattrs :bg-cyan {:fill "#a0fafa"})
(defattrs :bg-purple {:fill "#e4b5f7"})

(defn draw-group-label-header
  [span label]
  (draw-box (text label [:math {:font-size 12}]) {:span span :borders #{} :height 14}))

(defn draw-remotedb-header
  [kind args]
  (draw-column-headers)
  (draw-group-label-header 5 "start")
  (draw-group-label-header 5 "TxID")
  (draw-group-label-header 3 "type")
  (draw-group-label-header 2 "args")
  (draw-group-label-header 1 "tags")
  (next-row 18)

  (draw-box 0x11 :bg-green)
  (draw-box 0x872349ae [{:span 4} :bg-green])
  (draw-box 0x11 :bg-yellow)
  (draw-box (text "TxID" :math) [{:span 4} :bg-yellow])
  (draw-box 0x10 :bg-pink)
  (draw-box (hex-text kind 4 :bold) [{:span 2} :bg-pink])
  (draw-box 0x0f :bg-cyan)
  (draw-box (hex-text args 2 :bold) :bg-cyan)
  (draw-box 0x14 :bg-purple)

  (draw-box (text "0000000c" :hex [[:plain {:font-weight "light" :font-size 16}] " (12)"]) [{:span 4} :bg-purple])
  (draw-box (hex-text 6 2 :bold) [:box-first :bg-purple])
  (doseq [val [6 6 3 6 6 6 6 3]]
    (draw-box (hex-text val 2 :bold) [:box-related :bg-purple]))
  (doseq [val [0 0]]
    (draw-box val [:box-related :bg-purple]))
  (draw-box 0 [:box-last :bg-purple]))

(draw-remotedb-header 0x4702 9)

(draw-box 0x11)
(draw-box 0x2104 {:span 4})
(draw-box 0x11)
(draw-box 0 {:span 4})
(draw-box 0x11)
(draw-box (text "length" [:math] [:sub 1]) {:span 4})
(draw-box 0x14)

(draw-box (text "length" [:math] [:sub 1]) {:span 4})
(draw-gap "Cue and loop point bytes")

(draw-box nil :box-below)
(draw-box 0x11)
(draw-box 0x36 {:span 4})
(draw-box 0x11)
(draw-box (text "num" [:math] [:sub "hot"]) {:span 4})
(draw-box 0x11)
(draw-box (text "num" [:math] [:sub "cue"]) {:span 4})

(draw-box 0x11)
(draw-box (text "length" [:math] [:sub 2]) {:span 4})
(draw-box 0x14)
(draw-box (text "length" [:math] [:sub 2]) {:span 4})
(draw-gap "Unknown bytes" {:min-label-columns 6})
(draw-bottom)


```

</details>

こんなコマンドで svgファイルが取得出来る
```shell
curl -X POST http://localhost:8000/bytefield      -H "Content-Type: text/plain"      -H "Accept: image/svg+xml"      --data-binary "@example.bf"      -o diagram.svg
```

`/` 以降の部分を変更することで例えば以下のようにplantumlも取ってこれる

```shell
curl -X POST http://localhost:8000/plantuml      -H "Content-Type: text/plain"      -H "Accept: image/svg+xml"      --data-binary "@example.puml"      -o diagram.svg
```

 `--data-binary` で指定する場合  `@` + ファイル名 となるようにする