Concept internals
Markdown の書式設定
OpenClaw は、チャンネル固有の出力をレンダリングする前に、送信 Markdown を共有の中間表現 (IR)へ変換します。IR はプレーンテキストとスタイル/リンクのスパンを保持するため、 1 回の解析ですべてのチャンネルに対応でき、チャンク分割によってスパン途中の 書式が分断されることもありません。
パイプライン
- Markdown を IR に解析(
markdownToIR)- プレーンテキストに加えて、スタイルスパン (太字、斜体、取り消し線、コード、コードブロック、スポイラー、引用、 見出し 1~6)とリンクスパンを生成します。オフセットは UTF-16 コード単位であるため、 Signal のスタイル範囲をその API と直接整合させられます。テーブルは、チャンネルが テーブルモードを有効にした場合にのみ解析されます。 - IR をチャンク分割(
chunkMarkdownIR/renderMarkdownIRChunksWithinLimit)- 分割はレンダリング前の IR テキストに対して行われるため、インラインスタイルと リンクは境界をまたいで壊れるのではなく、チャンクごとに切り分けられます。
- チャンネルごとにレンダリング(
renderMarkdownWithMarkers)- スタイルマーカーの マップにより、スパンをチャンネル固有のマークアップへ変換します。
| チャンネル | レンダラー | 備考 |
|---|---|---|
| Slack | mrkdwn トークン(*太字*、_斜体_、`コード`、コードフェンス) |
リンクは <url|label> になります。二重リンクを避けるため、解析時は自動リンクを無効にします |
| Telegram | HTML タグ(<b>、<i>、<s>、<code>、<pre><code>、<a href>、<tg-spoiler>) |
richMessages が有効な場合は、リッチメッセージのテーブルと見出し(<h1>~<h6>)にも対応します |
| Signal | プレーンテキスト + text-style 範囲 |
ラベルが URL と異なる場合、リンクは label (url) としてレンダリングされます |
| Discord、WhatsApp、iMessage、Microsoft Teams、その他のチャンネル | プレーンテキスト | IR ベースのスタイルはありません。Markdown テーブル変換は引き続き convertMarkdownTables により実行されます |
IR の例
入力 Markdown: OC_I18N_900000 IR(概略): OC_I18N_900001
テーブルの処理
markdown.tables は、チャンネルごと、必要に応じてアカウントごとに、
チャンネルが Markdown テーブルを変換する方法を制御します。
| モード | 動作 |
|---|---|
code |
コードブロック内に整列済み ASCII テーブルとしてレンダリングします(フォールバックの既定値) |
bullets |
各行を label: value の箇条書きへ変換します |
block |
トランスポートが対応している場合はネイティブテーブルを維持し、それ以外は code にフォールバックします |
off |
テーブル解析を無効にし、生のテーブルテキストを変更せずに通過させます |
チャンネル別 Plugin の既定値:Signal、WhatsApp、Matrix の既定値は
bullets、Mattermost の既定値は off、Telegram の既定値は block です
(アカウントで richMessages が有効でない限り、code として解決されます)。
明示的な Plugin の既定値がないチャンネルは、code にフォールバックします。
OC_I18N_900002
チャンク分割の規則
- チャンク制限はチャンネルアダプター/設定から取得され、レンダリング済み出力ではなく IR テキストに適用されます。
- フェンス付きコードブロックは、末尾の改行を含む 1 つのブロックとして維持されるため、 チャンネルは閉じフェンスを正しくレンダリングできます。
- リストと引用の接頭辞は IR テキストの一部であるため、チャンク分割によって 接頭辞の途中で分断されることはありません。
- インラインスタイルがチャンク間で分断されることはありません。レンダラーは、 次のチャンクの先頭で開いていたスタイルを再度開始します。
チャンネル間のチャンク境界と配信動作については、 ストリーミングとチャンク分割を参照してください。
リンクポリシー
- Slack:
[label](url)-><url|label>。裸の URL はそのまま維持されます。 - Telegram:
[label](url)-><a href="url">label</a>(HTML 解析モード)。 - Signal: ラベルがすでに URL と一致している場合を除き、
[label](url)->label (url)になります。
スポイラー
スポイラーマーカー(||spoiler||)は、Signal では SPOILER
スタイル範囲に、Telegram では <tg-spoiler> に対応付けて解析されます。
その他のチャンネルでは、||...|| はプレーンテキストとして扱われます。
チャンネルフォーマッターの追加または更新
- 一度だけ解析:チャンネルに適したオプション
(
autolink、headingStyle、blockquotePrefix、tableMode)を渡して、markdownToIR(...)を使用します。 - レンダリング:
renderMarkdownWithMarkers(...)とスタイルマーカーのマップ (または Signal のようなトランスポート向けのカスタムスタイル範囲ロジック)を使用します。 - チャンク分割:各チャンクをレンダリングする前に、
chunkMarkdownIR(...)またはrenderMarkdownIRChunksWithinLimit(...)を使用します。 - アダプターを接続:送信処理経路から新しいチャンク分割処理とレンダラーを 呼び出すようにします。
- テスト:書式テストを行い、チャンネルがチャンク分割する場合は 送信配信テストも実施します。
よくある注意点
- Slack の山括弧トークン(
<@U123>、<#C123>、<https://...>)は、 エスケープ後も維持する必要があります。生の HTML は引き続き安全に エスケープする必要があります。 - Telegram HTML では、マークアップの破損を避けるため、タグ外のテキストを エスケープする必要があります。
- Signal のスタイル範囲では、コードポイントのオフセットではなく、 UTF-16 オフセットを使用します。
- 閉じマーカーが独立した行に配置されるように、フェンス付きコードブロックの 末尾の改行を維持してください。
関連項目
Was this useful?