リッチテキストの形(Hako Rich Text v1)
リッチエディタのフィールドの値は、Hako Rich Text v1 という形の JSON で読み書きします。形は ProseMirror と Tiptap の JSON(type attrs content marks text)と同じです。Tiptap の generateHTML などに、変えずにそのまま渡せます。
| 場面 | 形 |
|---|---|
| Content API で読む | 既定でこの形(正規形)。richText=html を付けると HTML の文字列 |
| Content API と Admin API で書く | この形の JSON か、HTML の文字列 |
{ "type": "doc", "content": [ { "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "はじめに" }] }, { "type": "paragraph", "content": [ { "type": "text", "text": "これは" }, { "type": "text", "text": "太字", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "と" }, { "type": "text", "text": "リンク", "marks": [ { "type": "link", "attrs": { "href": "https://example.com" } } ] }, { "type": "text", "text": "です。" } ] } ]}空の文書は { "type": "doc" } です。
ノードを置ける場所
Section titled “ノードを置ける場所”| 場所 | 置けるもの |
|---|---|
文書(doc)の直下 |
ブロック(0 個以上) |
| ブロック | paragraph heading bulletList orderedList blockquote codeBlock horizontalRule image table embed |
paragraph heading の中 |
インライン(text hardBreak。0 個以上) |
bulletList orderedList の中 |
listItem(1 個以上) |
listItem blockquote の中 |
ブロック(1 個以上) |
codeBlock の中 |
マークのない text(0 個以上) |
table の中 |
tableRow(1 個以上) |
tableRow の中 |
tableCell か tableHeader(1 個以上) |
tableCell tableHeader の中 |
ブロック(1 個以上) |
ノードの属性
Section titled “ノードの属性”既定の値の属性は、正規形では省かれます。省いたときは既定の値として扱います。
| ノード | 属性 | 型 | 必須 | 既定 | 説明 |
|---|---|---|---|---|---|
paragraph |
textAlign |
"left" "center" "right" "justify" |
なし | 文字揃え | |
heading |
level |
1〜6 の整数 | ○ | 見出しのレベル。正規形でも必ず付く | |
textAlign |
paragraph と同じ |
なし | 文字揃え | ||
orderedList |
start |
整数 | 1 |
始めの番号 | |
codeBlock |
language |
文字列 | なし | 言語。HTML では <code class="language-xxx"> になる |
|
image |
src |
空でない文字列 | ○ | 画像の URL | |
alt title |
文字列 | なし | 代替テキスト、タイトル | ||
width height |
正の整数 | なし | 幅、高さ(px) | ||
embed |
src |
空でない文字列 | ○ | 埋め込む URL。HTML では、https のものだけを iframe にする |
|
tableCell tableHeader |
colspan rowspan |
1 以上の整数 | 1 |
セルの結合 | |
colwidth |
正の整数の配列 | なし | 列の幅(px)。結合したセルは、列ごとの幅を並べる | ||
text |
text |
空でない文字列 | ○ | 本文。attrs の中ではなく、ノードのキー |
|
marks |
マークの配列 | なし | 下の「マーク」 |
hardBreak(改行)、horizontalRule(水平線)、bulletList、listItem、blockquote、table、tableRow には、属性はありません。
| マーク | 属性 | 説明 |
|---|---|---|
link |
href(必須。空でない文字列)、target(既定 "_blank")、rel(既定 "noopener noreferrer nofollow")、class |
リンク。HTML では、javascript: などの危ないスキームの href は空になる |
bold |
太字 | |
code |
インラインのコード | |
italic |
斜体 | |
strike |
取り消し線 | |
underline |
下線 | |
customClass |
class(必須。空でない文字列) |
選んだ文字にクラスを付ける(<span class="...">) |
表の並び(link → bold → code → italic → strike → underline → customClass)が、HTML での入れ子の順(外側から)で、正規形での marks の並び順です。
書き込んだ値は、次の決まりでそろえてから保存されます。Content API で返る値も、この形です。同じ中身なら同じ JSON になります。
| 決まり | 例 |
|---|---|
既定の値の属性、null の属性、空の文字列の省ける属性を省く |
{"start": 1} → 省く、{"textAlign": null} → 省く |
属性がなくなったら attrs ごと省く |
{"type": "paragraph", "attrs": {}} → {"type": "paragraph"} |
| マークを上の表の順に並べ、同じ種類のマークは最初の 1 つだけ残す | [italic, bold] → [bold, italic] |
| 隣り合うテキストのマークが同じなら 1 つにまとめる | "太" と "字"(どちらも bold)→ "太字" |
| 空のテキストを省く | {"type": "text", "text": ""} → 省く |
空の content と空の marks を省く |
{"type": "paragraph", "content": []} → {"type": "paragraph"} |
| 知らない属性と、ノード・マークの知らないキーを捨てる | image の attrs の foo → 捨てる |
キーの順を type → attrs → content にする(テキストは type → text → marks) |
知らない属性はエラーにせず、捨てます。知らないノードとマークはエラーになります(下の「書き込みのエラー」)。
HTML で書き込むとき
Section titled “HTML で書き込むとき”HTML の文字列を送ると、Hako Rich Text v1 の JSON に変えて保存します。エディタで扱えない要素は、中身だけが残ります。<script> などは捨てられます。HTML ではエラーになりません。
書き込みのエラー
Section titled “書き込みのエラー”JSON を送ったときに決まりに合わないと、400 が返ります。issues に、フィールドの中の場所(path)と理由(message)が並びます。エラーは、最初の 1 つで止めず、まとめて返ります。
Content API では、次のように返ります。
{ "error": { "code": "bad_request", "message": "body.content[0].attrs.level: must be an integer from 1 to 6", "details": { "issues": [ { "path": "body.content[0].attrs.level", "message": "must be an integer from 1 to 6" } ] } }}Admin API では、message と issues が本文のいちばん上に入ります。カスタムフィールドや繰り返しの中では、path は blocks[0].caption.content[0] のようにたどります。
理由(message) |
起きるとき |
|---|---|
must be a document ({"type": "doc", ...}) |
値が type: "doc" のオブジェクトでない(path はフィールド ID だけ) |
must be a node with a "type" |
ノードに type がない |
unknown node type "mystery" |
知らないノード |
"paragraph" is not allowed here (expected inline content (text, hardBreak)) |
置けない場所にある |
"text" is not allowed here (expected block content) |
文書の直下にテキストを置いた |
bulletList must contain at least 1 listItem |
中身が足りない(「1 個以上」の場所) |
must be an array |
content や marks が配列でない |
text must be a string |
text が文字列でない |
must be an integer from 1 to 6 |
見出しのレベルが 1〜6 でない |
must be one of left, center, right, justify |
文字揃えの値が違う |
is required (a non-empty string) |
必須の属性(src href class)がない、または空 |
must be a string / must be an integer / must be a positive integer |
属性の型が違う |
unknown mark type "glitter" |
知らないマーク |
must be a mark with a "type" |
マークに type がない |
- v1 の中では、ノード、マーク、省ける属性を足すことがあります。足したものは、省いたときに今までと同じ意味になります
- v1 の中では、今あるノード、マーク、属性の名前と意味、既定の値、置ける場所、正規形の決まりは変えません
- 形を壊す変更は、新しい形として足し、v1 もしばらく返せるようにします。更新履歴 で知らせます
サイトで描くときは、知らないノードは中身(content)だけを描き、知らないマークと属性は無視するように作っておくと、v1 の中での追加で壊れません。