目標
- 一貫性: 1 回のパース(解析)で、複数のレンダラーに対応。
- 安全なチャンク化: レンダリング前にテキストを分割することで、太字などのスタイルが分割位置で壊れるのを防ぎます。
- チャネルへの最適化: 同じ IR から、Slack の mrkdwn、Telegram の HTML、Signal のスタイル範囲へと、Markdown を再解析することなくマッピングします。
パイプライン
- Markdown -> IR の解析
- IR は、プレーンテキストにスタイル範囲(太字/斜体/打ち消し線/コード/ネタバレ)とリンク範囲を組み合わせたデータ構造です。
- オフセット値は UTF-16 コード単位で保持されるため、Signal のスタイル範囲指定とも正確に一致します。
- テーブル(表)の解析は、そのチャネルがテーブル変換を有効にしている場合にのみ行われます。
- IR のチャンク化 (フォーマット優先)
- レンダリングを行う前の IR 段階でテキストを分割します。
- インラインのスタイル指定が分割位置をまたぐことはありません。スタイル範囲はチャンクごとに適切に切り分けられます。
- チャネルごとのレンダリング
- Slack: mrkdwn トークン(太字/斜体/打ち消し線/コード)、リンクは
<url|ラベル>形式。 - Telegram: HTML タグ (
<b>,<i>,<s>,<code>,<pre><code>,<a href>)。 - Signal: プレーンテキスト +
text-style範囲指定。リンクはラベルが URL と異なる場合にラベル (url)形式になります。
- Slack: mrkdwn トークン(太字/斜体/打ち消し線/コード)、リンクは
IR の例
入力 Markdown:利用されている場所
- Slack, Telegram, Signal のアウトバウンド用アダプターは、この IR を元にメッセージを生成します。
- 他のチャネル (WhatsApp, iMessage, MS Teams, Discord) は、現在もプレーンテキストまたは独自のフォーマットルールを使用していますが、有効な場合はチャンク化の前に Markdown テーブルの変換が適用されます。
テーブル(表)の扱い
Markdown のテーブルは、チャットクライアントによってサポート状況が大きく異なります。markdown.tables 設定を使用して、チャネルごと(あるいはアカウントごと)に変換方法を制御できます。
code: テーブルをコードブロックとしてレンダリングします(ほとんどのチャネルのデフォルト)。bullets: 各行を箇条書き(弾丸リスト)に変換します(Signal, WhatsApp のデフォルト)。off: テーブルの解析と変換を無効にします。元のテーブルテキストがそのまま送信されます。
チャンク化(分割)のルール
- チャンクの文字数制限はチャネルアダプターや構成から取得され、IR のプレーンテキストに対して適用されます。
- コードブロック(Code fence)は、末尾の改行と共に 1 つのブロックとして保持され、チャネル側で正しく表示されるよう配慮されます。
- リストの接頭辞や引用(blockquote)の接頭辞は IR テキストの一部として扱われるため、接頭辞の途中で分割されることはありません。
- インラインスタイル(太字/斜体/打ち消し線/インラインコード/ネタバレ)がチャンクをまたいで分割されることはありません。レンダラーは、各チャンクの開始時にスタイルを再開(開き直し)します。
リンクのポリシー
- Slack:
[ラベル](url)-><url|ラベル>。URL 単体はそのまま保持されます。二重リンクを防ぐため、解析中の自動リンク機能はオフになっています。 - Telegram:
[ラベル](url)-><a href="url">ラベル</a>(HTML パースモード)。 - Signal:
[ラベル](url)-> ラベルが URL と一致しない限りラベル (url)。
ネタバレ (Spoilers)
ネタバレマーカー (||テキスト||) は、Signal 用にのみ解析され、SPOILER スタイル範囲にマップされます。他のチャネルではプレーンテキストとして扱われます。
新しいチャネルフォーマッタの追加手順
- 一度だけパース: 共有ヘルパー
markdownToIR(...)を使用します。チャネルに適したオプション(自動リンク、見出しスタイル、引用接頭辞など)を指定してください。 - レンダリング:
renderMarkdownWithMarkers(...)とスタイルマーカーのマッピング(または Signal 用のスタイル範囲)を実装します。 - チャンク化: レンダリングの前に
chunkMarkdownIR(...)を呼び出し、分割された各チャンクをレンダリングします。 - アダプターへの接続: チャネルのアウトバウンドアダプターを更新し、新しいチャンカーとレンダラーを使用するように変更します。
- テスト: フォーマットテストを追加・更新します。チャンク化を使用するチャネルの場合は、配信テストも追加してください。
よくある落とし穴
- Slack の角括弧トークン (
<@U123>,<#C123>,<https://...>) は保持される必要があります。生身の HTML は安全にエスケープしてください。 - Telegram の HTML モードでは、タグ以外のテキストをエスケープしないと表示が壊れる原因になります。
- Signal のスタイル範囲は UTF-16 オフセットに基づいています。コードポイントベースのオフセットを使用しないでください。
- フェンスで囲まれたコードブロックの末尾の改行を保持してください。そうしないと、閉じマーカーが専用の行に配置されない可能性があります。