JSON Canvas の仕様

2024 年 3 月 11 日、Obsidian は **[JSON Canvas](https://jsoncanvas.org/spec/1.0/)** と呼ばれる、**無限キャンバス**で扱うデータを保存するためのファイルの仕様を発表しました。

:::note
無限キャンバス (infinite canvas) とは、無制限の平面に描画できるキャンバスのことを指します。[JSON Canvas のデモ](https://jsoncanvas.org/)で確認できます。
:::

以下のように、JSON Canvas に関連するすべてのリソースは MIT ライセンスの下でオープンソースです。

> All the resources associated with JSON Canvas are open source under the MIT license, and can be found on [GitHub](https://github.com/obsidianmd/jsoncanvas).
> 
> [Announcing JSON Canvas: an open file format for infinite canvas data - Obsidian](https://obsidian.md/blog/json-canvas/)

本記事では、JSON Canvas の仕様の解説を行います。


# JSON Canvas とは
2024 年 3 月 11 日にリリースされたバージョン 1.0 について解説します。

https://jsoncanvas.org/spec/1.0/


JSON Canvas という名前からも想像ができますが、`JSON` 形式で記述します。JSON Canvas ファイルには [`.canvas`](https://github.com/obsidianmd/jsoncanvas/blob/main/readme.md) 拡張子が使用されます。

```json: sample.canvas 
{
  "nodes": [
    {
      "id": "nav",
      "type": "text",
      "x": 336,
      "y": 36,
      "width": 182,
      "height": 168,
      "text": "Learn more:\n- [Apps](/docs/apps)\n- [Spec](/spec/1.0)\n- [GitHub](https://github.com/obsidianmd/jsoncanvas)"
    },
    {
      "id": "logo",
      "type": "file",
      "x": 36,
      "y": 48,
      "width": 176,
      "height": 68,
      "file": "logo.svg"
    }
  ],
  "edges": [
    {
      "id": "edge-logo-nav",
      "fromNode": "logo",
      "fromSide": "right",
      "fromEnd": "none",
      "toNode": "nav",
      "toSide": "left",
      "toEnd": "arrow"
    }
  ]
}
```



また、JSON Canvas を出発点に、異なる無限キャンバスツール間の相互運用性を確立することを目的としたオープンなワーキンググループである **Open Canvas Working Group (OCWG)** が発足しました。[tldraw](https://www.tldraw.com/) や [Excalidraw](https://excalidraw.com/), [statelyai](https://stately.ai/)、[KinopioClub](https://kinopio.club/hello-kinopio-vaUKyXEpYcoyZZi4hVEis)、[dxos_org](https://dxos.org/) などが参加しています。

https://www.canvasprotocol.org/



# JSON Canvas の仕様
## トップレベル (Top level)
JSON Canvas のトップレベルには、次の 2 つの配列が含まれています:

- `nodes` : キャンバス内のノードを格納する配列、必須ではない
- `edges` : ノード間を接続するエッジを格納する配列、必須ではない

トップレベルとは、JSON Canvas 内のネストが一番浅いの要素を指します。

```json
{
  "nodes": [],
  "edges": []
}
```


## ノード (Nodes)
ノードはキャンバス内のオブジェクトです。ノードにはテキスト、ファイル、リンク、またはグループが含まれます。

### ジェネリックノード (Generic node)
すべてのノードには、次の属性が含まれます:

| 属性   | 必須/任意 | データ型 | 説明                                      |
|--------|--------|----------|--------------------------------|
| id     | 必須     | 文字列   | ノードのユニークなID                      |
| type   | 必須     | 文字列   | ノードの種類<br>(`text`,`file`,`link`,`group`)|
| x      | 必須     | 整数     | ノードの x 座標（ピクセル単位）           |
| y      | 必須     | 整数     | ノードの y 座標（ピクセル単位）           |
| width  | 必須     | 整数     | ノードの幅（ピクセル単位）               |
| height | 必須     | 整数     | ノードの高さ（ピクセル単位）             |
| color  | 任意     | canvasColor   | ノードの色           |

`canvasColor` は **色 (Color)** のセクションを参照してください。

### テキストタイプのノード (Text type nodes)
テキストタイプは、テキストを保持します。テキストタイプのノードには、次の属性が含まれます:

| 属性   | 必須/任意 | データ型 | 説明|
|--------|-----------------|----------|---------|
|text|必須|文字列|Markdown 構文を使用したプレーンテキスト|

```json
{
  "nodes": [
    {
      "id": "sampletext",
      "type": "text",
      "x": 365,
      "y": 24,
      "width": 120,
      "height": 45,
      "text": "**strong** text"
    }
  ],
}
```

### ファイルタイプのノード (File type nodes)
ファイルタイプのノードは他のファイル（画像やビデオなど）へのパスです。ファイルタイプのノードには、次の属性が含まれます:


| 属性   | 必須/任意 | データ型 | 説明|
|--------|-----------------|----------|---------|
|file	|必須|文字列|システム内のファイルへのパス|
|subpath|任意|文字列|	ファイルのサブパス。見出しやブロックにリンクすることができます。<br>常に `#` で始まります。|

```json
{
  "nodes": [
    {
      "id": "readme",
      "type": "file",
      "x": -357,
      "y": 246,
      "width": 400,
      "height": 500,
      "file": "README.md"
    },
  ]
}
```

### Link type nodes
Link type nodes reference a URL, and include the following attribute:

リンクタイプのノードは URL を参照します。リンクタイプのノードには、次の属性が含まれます:
| 属性   | 必須/任意 | データ型 | 
|--------|-----------------|----------|
|url	|必須|文字列|

### グループタイプのノード (Group type nodes)
グループタイプのノードは、それに含まれるノードの視覚的なコンテナとして使用されます。リンクタイプのノードには、次の属性が含まれます:


| 属性   | 必須/任意 | データ型 | 説明|
|--------|-----------------|----------|---------|
|label	|任意|文字列|グループのテキストラベル|
|background	|任意|文字列|背景画像へのパス|
|backgroundStyle|任意|文字列|背景画像のレンダリングスタイル|

`backgroundStyle` で有効な値:

| スタイル     | 説明                                                    |
|------------|---------------------------------------------------------|
| cover      | ノードの全幅と全高を埋める                               |
| ratio      | 背景画像のアスペクト比を保持する                         |
| repeat     | 画像を x と y の両方の方向に繰り返すパターンとして使用する |

- [background-size - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/background-size)
- [background-repeat - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/background-repeat)

:::note warn
グループタイプのノードは、**視覚的な**コンテナであり、データ上で他のノードと親子関係をもっているわけではありません。ただの背景です。
:::

## エッジ (Edges)
エッジは、あるノードを別のノードに接続する線です。

以下は、Markdownのテーブル形式でエッジの属性を示したものです：

| 属性   | 必須/任意 | データ型   | 説明|
|-------|-----------------|------------|----------------------------|
| id         | 必須            | 文字列     | エッジの一意のID|
| fromNode   | 必須            | 文字列     | 接続が開始されるノードのID|
| fromSide   | 任意      | 文字列     | エッジが始まる側の位置|
| fromEnd    | 任意      | 文字列     | エッジの始点の形状|
| toNode     | 必須            | 文字列     | 接続が終了するノードのID|
| toSide     | 任意      | 文字列     | エッジが終了する側の位置|
| toEnd      | 任意      | 文字列     | エッジの終点の形状|
| color      | 任意      | `canvasColor` | 線の色|
| label      | 任意      | 文字列     | エッジのテキストラベル|


`fromSide` と `toSide` は `top`、`right`、`bottom`、`left` のいずれかの値、`fromEnd` と `toEnd` は `none` もしくは `arrow` のいずれかの値が有効です。

```json
{
  "nodes": [
  ],
  "edges": [
    {
      "id": "edge-logo-nav",
      "fromNode": "logo",
      "fromSide": "right",
      "fromEnd": "none",
      "toNode": "nav",
      "toSide": "left",
      "toEnd": "arrow"
    },
  ]
}
```
## 色 (Color)
色の種類は、ノードやエッジの色データをエンコードするために使用されます。

色の属性は**文字列**であることが望ましいです。
色は `hex` 形式で指定できます。例: `"#FFFFFF"`。

6つのプリセットカラー（あらかじめ定義された色）の値が存在し、次の数値にマップされます:

- `1`: red
- `2`: orange
- `3`: yellow
- `4`: green
- `5`: cyan
- `6`: purple

ただし、「`red` であれば **#FF0000**」のように、プリセットカラーの具体的な値が定められているわけではありません。アプリケーション開発者が、自分のアプリケーションのデザインに合った色を自由に設定できるようにするためです。