Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Tiptapで校正機能を作った時に考えたこと
Search
kirik
May 26, 2026
Technology
110
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
950
Tiptapで実現する堅牢で柔軟なエディター開発
kirik
1
520
Recoilを剥がしている話
kirik
5
12k
Other Decks in Technology
See All in Technology
Claude Code on Bedrock で統制をかける小ネタ集/claude-code-bedrock-governance-tips
emiki
1
13k
(swiftc) NonCopyableとライフタイム修飾子
teamhimeh
0
150
クラウドエージェントの先駆け Devin。今も最先端を走る、他との違い
kinopeee
1
490
CTO Night & Day Morning Session_AI-DLC:生成 AI 時代の開発プロセス変革
pikosan0000
0
120
AI駆動開発におけるハーネスエンジニアリングの勘所
tsukuboshi
1
820
なぜAI時代に技術記事を書くのか?どう書くのか? 〜bunの場合〜
kworkdev
PRO
0
160
AWSセキュリティで活用するAI機能
cmusudakeisuke
1
340
これだけは抑えておきたい!後悔しない re:Invent の過ごし方 / AWS re:Invent 2026 prestudy
kinunori
2
490
ドメインエキスパートは いかにして AI を使って プロダクト開発をするか
tatsuyashiraishi
1
220
キヨさん、67歳。AIコーディングエージェントに挑む
happysamurai294
0
110
構造で守らせる データ・法務・セキュリティ・開発チームとつくる AI時代のデータガバナンスとデータ基盤のリアル
legalontechnologies
PRO
2
620
Devin プロモコードの受け取りと適用(2026.10.27)
kinopeee
1
650
Featured
See All Featured
Measuring & Analyzing Core Web Vitals
bluesmoon
9
1k
GitHub's CSS Performance
jonrohan
1033
470k
Building Applications with DynamoDB
mza
96
7.3k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.9k
Unsuck your backbone
ammeep
672
58k
How STYLIGHT went responsive
nonsquared
100
6.3k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.9k
Between Models and Reality
mayunak
4
520
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
280
Building Better People: How to give real-time feedback that sticks.
wjessup
371
20k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
WCS-LA-2024
lcolladotor
0
930
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 のデフォルト挙動 を柔軟に変えられる