Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Tiptapで校正機能を作った時に考えたこと
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
kirik
May 26, 2026
Technology
80
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Tiptapで校正機能を作った時に考えたこと
https://web-study.connpass.com/event/391357/
kirik
May 26, 2026
More Decks by kirik
See All by kirik
Recoil脱却の現状と挑戦
kirik
2
870
Tiptapで実現する堅牢で柔軟なエディター開発
kirik
1
460
Recoilを剥がしている話
kirik
5
12k
Other Decks in Technology
See All in Technology
攻撃者視点で考えるDetection Engineering
cryptopeg
3
2.1k
現場のトークンマネジメント
dak2
1
190
サイバーエージェントにおけるAI推進戦略と変革への取り組み
shotatsuge
0
530
GitHub Copilot app最速の発信の裏側
tomokusaba
1
250
フィジカル版Github Onshapeの紹介
shiba_8ro
0
320
コミットの「なぜ」を読む
ota1022
0
120
ロボティクスの技術 / Robotics Technology
ks91
PRO
0
130
WebGIS AI Agentの紹介
_shimizu
0
550
AIチャット検索改善の3週間
kworkdev
PRO
2
170
フルAIで個人開発して学んだあれこれ / yuruai vol.1
isaoshimizu
0
110
From Prompt Engineering to Loop Engineering
shibuiwilliam
1
160
5分でわかるDuckDB Quack
chanyou0311
2
250
Featured
See All Featured
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
1
3.6k
The Spectacular Lies of Maps
axbom
PRO
1
820
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
230
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
66
55k
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
150
Thoughts on Productivity
jonyablonski
76
5.2k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
240
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
Test your architecture with Archunit
thirion
1
2.3k
Transcript
Tiptap で校正機能を作った時に 考えたこと Rich Text Editor Study 2026/5/19 (火) 株式会社PR
TIMES @kiririLee
• 校正機能について • Tiptap について ◦ Tiptap が持つドキュメント構造 • API
レスポンスをHTMLにマッピングする • 校正箇所が重なった場合を表現する • ProseMirror Plugin で細かい挙動を制御する • まとめ
校正機能について
• 2024年9月12日にリリースされた 機能 • プレスリリース入稿時のエディター上 で文章を校正し、誤りを修正できる 機能 • 一般的に校正対象となるようなルー ルとプレスリリースに特化したルー
ルが組まれている https://prtimes.jp/main/html/rd/p/000001456.000000112.html
デモ動画 https://youtu.be/n0jryll2o1o
Tiptap について
• Tiptap は ProseMirror のラッパーであり、内部的には ProseMirror が 独自のツリーを管理している • ProseMirror
は HTML を直接操作するのではなく、独自のドキュメント構 造に落とし込み、エディタ上の状態と合わせて扱う。
Node と Mark このスライドでは ProseMirror が持つドキュメント構造全体を 「ProseMirror Document Model」 と呼ぶようにします。
API レスポンスを HTML にマッピングする
• 文章の校正処理はAPIに投げている。校正結果は JSON であるため、校正箇所を 示す要素を HTML にマッピングする • 校正箇所を示す要素は proofreading
要素を使用している ◦ ブラウザ標準で proofreading 要素はなく、当社で拡張している要素 ※ マッピング処理は Tiptap が関係しない
• parseHTML (と renderHTML) が HTML 構造を ProseMirror の Mark
に変換するための橋渡し になっているメソッド • HTMLドキュメント上の proofreading 要素を Proofreading Mark として紐づけ、その他のプ ロパティやメソッドなどで 振る舞いを実装 proofreading 要素に対してエディターの振る舞いを付与
• Tiptap から提供される Mark.create で特定の HTML 要素を指定して、 ProseMirror Document Model
の一部として読み込む(パースする) • Mark.create の戻り値は Tiptap では Extension という概念で管理される • 校正機能はコードベース上で proofreading 要素をパースして、振る舞いを付与し た Extension を ProofreadingExtension と呼んでいる proofreading 要素に対してエディターの振る舞いを付与
• 最大のポイントは excludes オプションの 設定 • この後のスライドで登場す る要件を実装するのに必 要不可欠
校正箇所が重なった場合を表現する
校正の指摘箇所が重なる場合のデモ https://youtu.be/MMX-cjl8Jxk
校正箇所は重なる場合がある • 指摘箇所がドキュメント上で重なる。これを視覚的にユーザーにフィードバック したい
• proofreading 要素をネストし、CSS で透明度 20% を重ねる
この時の同一要素のネストが excludes で設定出来る • デフォルトの設定だとできない • この設定を見つけるのにかなり時 間がかかった
設定しないとパース時にフラットにノーマライズされる パースされる前 パースされた後
同一要素をネストする設定方法がすぐに分からなかった • 最初は Tiptap のドキュメント を参照したが、空文字の指定で同一要素のネス トができることは書かれていなかった • 実装した当時(2024年6月ごろ)は、ChatGPT に聞いても教えてくれず、
inline 化した Node で実装する方法しか提案されなかった • Tiptap の作者が同一要素のネストを inline化した Node で実装することを 提案していた • PromseMirror のドキュメントには書かれていたが inline化した Node に よる実装に着手してから気がつく
inline node での実装提案 https://github.com/ueberdosis/tiptap/ discussions/2270
開発当初は inline node で実装していた • 開発当初は、Tiptap 作者が同一要素のネスト実装として inline node を提案し
ていたため、それを採用して開発を進めていた • しかし、改行ができないなどテキスト編集機能に制約が多く、不足を補うための複 雑な実装が増加 • その結果、システムテストで多数の不具合が発生し、inline node ベースの実装 には限界があると判断 • 最終的には、excludes: '' を設定した Mark で再実装 「Mark + excludes: ''」 で実装するというのは、 校正機能開発において最も時間を要した設計判断
None
この節のまとめ
ProseMirror Plugin で 細かい挙動を制御する
実装が必要だった要件 • 校正箇所で Backspace キーが押下された場合、該当する校正箇所を 削除する • 校正の指摘が重複している位置で Backspace キーが押下された場
合、重複しているすべての校正箇所を削除する
Mark のデフォルト挙動デモ https://youtu.be/5Ud2BzmHy3M
PM Plugin 実装後の挙動デモ https://youtu.be/ikJTJ5xaMFs
要件に合わせてMark のデフォルト挙動を変える実装をする
実装の概要 • handleKeyDown で Backspace を検知 • ProseMirror のメソッドである tr.removeMark で
Node に付与 されている Proofreading Mark を削除する • 校正の指摘が重複した場合は、親の Proofreading Mark も削除する
handleKeyDown で Backspace を拾う • Proofreading Extension に addProseMirrorPlugins を持たせ、その中で
ProseMirror の Plugin を返 す • Plugin の実装は完全に ProseMirror の世界 • Tiptap 越しではなく、 ProseMirror の API を直接 扱う
ProseMirror 3つの基本概念
Proofreading Mark を外す処理 • Backspace が押されたタイ ミングで、カーソル位置を取 得 • カーソル位置にある
Node を取得 • Node に付与されている Proofreading Mark を外 す
Proofreading Mark を外す処理 • カーソル位置から直接Mark を消すのではなく、 TextNode に付与されてい る Mark
を消す • 取得した TextNode には太 文字や下線など校正以外の別 Mark も同時に付与されてい る可能性があるため、校正の Mark のみ消す
基本的には Proofreading Mark を外す処理はこれでOK。 校正の指摘が重複した場合、 親の Proofreading Mark も 外す必要がある
HTML から見て親の Proofreading Mark も消す必要がある 子の Proofreading Mark は消せた
校正の指摘が重複したことを判定する • 校正の指摘が重複した、という判 定が必要 • この判定は、TextNode に付与 されている Proofreading Mark
が複数あるかどうかで判 定している
校正の指摘が重複したことを判定する https://youtu.be/jhiXM1EpVwY
コンソールに表示される TextNode HTML構造 • 「食べれ」の TextNode に親の id も付与されて いる
• この id を元に親の Mark を消す
校正の指摘が重複した時、 親の Proofreading Mark も外す HTML構造 ProseMirror Document Model
校正の指摘が重複した時、 親の Proofreading Mark も外す • Mark は TextNode に付与
される • ドキュメント上にある「子で取 得した id」 を持つ Mark が 付与されている TextNode をドキュメント上の Node を 走査して全て探す • 探し出した TextNode から Proofreading Mark を外 す
• 隣接する TextNode を取得するメソッドはあるが、2つ以上重複した場合 (可能性は非常に低い) を考えると横方向に全ての Proofreading Mark を辿るのが野暮ったい(コードから何をしているかが分かりにくい) •
ProseMirror Document Model の Mark はフラットな構造であるた め、ネストや親と子というような関係はない • Mark がフラットであるため、ドキュメント上の Node を全て走査する実装 は結構やる事が多い印象 • (他に良い方法があるかも? unsetMark メソッドで対応できないケースを 紹介したいけどまた今度) 校正の指摘が重複した時、 親の Proofreading Mark も外す
まとめ
• 校正箇所に対してエディター上ではテキスト操作がメインにな るため Mark を使う • 同一要素をネストして扱いたい場合には excludes オプ ションに空文字を指定する
• ProseMirror Plugin を使うと Mark のデフォルト挙動 を柔軟に変えられる