slack-markdown-parser 2.3.1__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/CHANGELOG.md +17 -0
- {slack_markdown_parser-2.3.1/slack_markdown_parser.egg-info → slack_markdown_parser-2.4.0}/PKG-INFO +22 -15
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/README-ja.md +18 -11
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/README.md +20 -12
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/docs/spec-ja.md +15 -6
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/docs/spec.md +15 -6
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/pyproject.toml +6 -4
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/slack_markdown_parser/__init__.py +1 -1
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/slack_markdown_parser/converter.py +723 -86
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0/slack_markdown_parser.egg-info}/PKG-INFO +22 -15
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/slack_markdown_parser.egg-info/requires.txt +0 -1
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/LICENSE +0 -0
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/MANIFEST.in +0 -0
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/setup.cfg +0 -0
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/slack_markdown_parser/py.typed +0 -0
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/slack_markdown_parser.egg-info/SOURCES.txt +0 -0
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/slack_markdown_parser.egg-info/dependency_links.txt +0 -0
- {slack_markdown_parser-2.3.1 → slack_markdown_parser-2.4.0}/slack_markdown_parser.egg-info/top_level.txt +0 -0
|
@@ -6,6 +6,23 @@ The format is based on Keep a Changelog, and the project follows Semantic Versio
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.4.0] - 2026-05-14
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added automatic richer Block Kit output for unambiguous standalone Markdown constructs, including image blocks, dividers, fenced code, simple quotes, and simple lists, while leaving Markdown headings in `markdown` blocks so Slack can preserve heading levels.
|
|
14
|
+
|
|
15
|
+
### Documentation
|
|
16
|
+
|
|
17
|
+
- Documented the Slack mobile `markdown` block limitation where list-item continuation lines are re-prefixed with the list marker, and linked the tracking issue so users can check upstream status instead of expecting a parser-side workaround.
|
|
18
|
+
|
|
19
|
+
## [2.3.2] - 2026-04-17
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Stopped `preserve_visual_blank_lines` from keeping ordered-list context open after list items, including continued items, nested lists, and ordered-list paragraph interruption edge cases.
|
|
24
|
+
- Avoided false-positive list-context detection for indented non-list lines and thematic breaks, so visible blank-line preservation no longer regresses on inputs like ` 1. not-a-list` or `- - -`.
|
|
25
|
+
|
|
9
26
|
## [2.3.1] - 2026-04-10
|
|
10
27
|
|
|
11
28
|
### Fixed
|
{slack_markdown_parser-2.3.1/slack_markdown_parser.egg-info → slack_markdown_parser-2.4.0}/PKG-INFO
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: slack-markdown-parser
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary: Convert LLM Markdown into Slack Block Kit
|
|
3
|
+
Version: 2.4.0
|
|
4
|
+
Summary: Convert LLM Markdown into Slack Block Kit messages
|
|
5
5
|
Author: darkgaldragon
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/darkgaldragon/slack-markdown-parser
|
|
@@ -26,12 +26,12 @@ Requires-Dist: pip-audit>=2.7.0; extra == "dev"
|
|
|
26
26
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
27
27
|
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
28
28
|
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
29
|
-
Requires-Dist: twine>=5.1.0; extra == "dev"
|
|
30
29
|
Dynamic: license-file
|
|
31
30
|
|
|
32
31
|
# slack-markdown-parser
|
|
33
32
|
|
|
34
|
-
`slack-markdown-parser` is a Python library that converts
|
|
33
|
+
`slack-markdown-parser` is a Python library that converts general Markdown generated by LLMs into Slack Block Kit messages built from `markdown`, `table`, and selected richer blocks so the output renders cleanly in Slack.
|
|
34
|
+
Basic headings and text formatting stay in `markdown` blocks, while constructs that benefit from dedicated Slack rendering, such as tables and simple standalone lists, are converted into native Block Kit blocks to produce a ChatGPT-like reading experience.
|
|
35
35
|
|
|
36
36
|
## Why this library exists
|
|
37
37
|
|
|
@@ -43,23 +43,25 @@ Many Slack AI bots have traditionally converted model output into Slack-specific
|
|
|
43
43
|
|
|
44
44
|
## Design approach
|
|
45
45
|
|
|
46
|
-
This library
|
|
46
|
+
This library combines Slack Block Kit's newer `markdown` block, `table` blocks, and richer block conversion for Markdown patterns that can be mapped safely.
|
|
47
47
|
|
|
48
48
|
| Problem | Approach |
|
|
49
49
|
|---|---|
|
|
50
|
-
| Extra conversion work |
|
|
51
|
-
| Formatting instability |
|
|
52
|
-
| No table syntax in `mrkdwn` | Detect Markdown tables and
|
|
50
|
+
| Extra conversion work | Slack `markdown` blocks can render a useful subset of Markdown other than tables, so LLM output can usually be sent with minimal rewriting instead of being converted into `mrkdwn`. |
|
|
51
|
+
| Formatting instability | Slack's `markdown` parser is not complete, so this library adds zero-width spaces (`U+200B`) around formatting-marker boundaries and uses visible spaces only for a few Japanese, Chinese, and Korean cases where that is still needed. This keeps emphasis stable across English and dense CJK text. |
|
|
52
|
+
| No table syntax in `mrkdwn` | Detect Markdown tables and render them as Slack `table` blocks, while also repairing common LLM-generated table issues such as missing pipes. |
|
|
53
|
+
| Rich LLM output | Convert standalone images, dividers, simple quotes, code fences, and simple lists into native Block Kit blocks where the Markdown structure is unambiguous. |
|
|
53
54
|
|
|
54
|
-
The goal is natural rendering on Slack, not full CommonMark or HTML fidelity.
|
|
55
|
+
The goal is natural rendering on Slack without exposing raw formatting markers, not full CommonMark or HTML fidelity.
|
|
55
56
|
If Slack itself does not support a construct in `markdown` blocks, this library prefers safe plain-text rendering or explicit `table` blocks over aggressive rewrites into old `mrkdwn`.
|
|
56
57
|
|
|
57
58
|
## Features
|
|
58
59
|
|
|
59
|
-
- Convert
|
|
60
|
+
- Convert general Markdown into Slack `markdown` blocks
|
|
60
61
|
- Convert Markdown tables into Slack `table` blocks
|
|
62
|
+
- Promote safe standalone Markdown constructs into richer Block Kit blocks: `image`, `divider`, and `rich_text`
|
|
61
63
|
- Repair common LLM table issues such as missing outer pipes, missing separator rows, mismatched column counts, and empty cells
|
|
62
|
-
- Split output into multiple Slack messages when needed to satisfy Slack's "one table per message"
|
|
64
|
+
- Split output into multiple Slack messages when needed to satisfy Slack's "one table per message" and per-message block-count constraints
|
|
63
65
|
- Remove ANSI/control characters and neutralize invalid Slack angle-bracket tokens before block generation
|
|
64
66
|
- Add zero-width spaces around inline formatting markers to reduce rendering issues outside fenced code blocks, while preserving English-like punctuation-only boundaries that Slack already renders reliably
|
|
65
67
|
- Add visible spaces for a small set of nested inline-code cases in dense Japanese, Chinese, and Korean text when zero-width spaces alone are not enough
|
|
@@ -74,7 +76,7 @@ The library is built around how Slack actually renders `markdown` and `table` bl
|
|
|
74
76
|
|
|
75
77
|
Slack updated its [`markdown` block docs](https://docs.slack.dev/reference/block-kit/blocks/markdown-block/)
|
|
76
78
|
and [changelog entry](https://docs.slack.dev/changelog/2026/03/06/block-kit-rich-text)
|
|
77
|
-
on March 6, 2026
|
|
79
|
+
on March 6, 2026 as it started supporting broader Markdown features such as headings, dividers, task lists,
|
|
78
80
|
native Markdown tables, and syntax-highlighted code blocks. In the Slack Web
|
|
79
81
|
workspace used for this project's April 8, 2026 validation, raw `markdown`
|
|
80
82
|
blocks rendered those constructs natively, including distinct heading levels
|
|
@@ -91,6 +93,7 @@ Reliable in current Slack rendering:
|
|
|
91
93
|
- Bare URLs, `<https://...>` style links, Markdown links, reference-style links, and mailto links
|
|
92
94
|
- Bullet lists, ordered lists, task lists, and simple blockquotes
|
|
93
95
|
- Explicit Slack `table` blocks generated from Markdown tables
|
|
96
|
+
- Explicit richer blocks generated from unambiguous Markdown, such as standalone images, code fences, simple lists, and simple quotes
|
|
94
97
|
- In environments where Slack has enabled the newer renderer, raw Markdown headings, dividers, and tables inside `markdown` blocks
|
|
95
98
|
|
|
96
99
|
Known Slack-side limitations:
|
|
@@ -101,19 +104,22 @@ Known Slack-side limitations:
|
|
|
101
104
|
- Raw Markdown tables inside `markdown` blocks now render in some newer Slack environments, but explicit Slack `table` blocks remain the reliable option across workspaces and delivery paths
|
|
102
105
|
- Markdown image syntax does not become an embedded image in `markdown` blocks
|
|
103
106
|
- Math, raw HTML, HTML comments, `<details>`, admonition syntax, and Mermaid are rendered as plain text or code, not as rich features
|
|
107
|
+
- Some newly documented Block Kit block types can be unavailable on a given `chat.postMessage` path; this library emits only a conservative subset of richer block types that has been validated through real Slack posting checks.
|
|
108
|
+
- The Slack **mobile** app re-prefixes the list marker onto each continuation line of a list item inside `markdown` blocks (e.g. `1. Heading` followed by an indented paragraph shows as `1. Heading` / `1.continuation` on mobile, while Slack desktop and Slack Web render the same payload correctly). This is a Slack client-side rendering behavior, not a parser bug. Tracking: [issue #45](https://github.com/darkgaldragon/slack-markdown-parser/issues/45).
|
|
104
109
|
|
|
105
110
|
What this library compensates for:
|
|
106
111
|
|
|
107
112
|
- Normalizes underscore emphasis (`_..._`, `__...__`) into Slack-friendly asterisk emphasis
|
|
108
113
|
- Wraps bare URLs into Slack-friendly `<https://...>` link form before sending `markdown` blocks
|
|
109
114
|
- Repairs malformed LLM-generated tables before converting them into Slack `table` blocks
|
|
115
|
+
- Converts unambiguous standalone Markdown constructs into native Block Kit blocks when that is safer than relying on raw `markdown` rendering
|
|
110
116
|
- Keeps table-like rows inside fenced code blocks out of table normalization
|
|
111
117
|
- Optionally turns internal blank lines into placeholder lines that keep paragraphs visibly separated in Slack `markdown` blocks
|
|
112
118
|
- Neutralizes invalid Slack angle-bracket tokens such as raw HTML-like tags
|
|
113
119
|
|
|
114
120
|
## Requirements
|
|
115
121
|
|
|
116
|
-
- Your Slack integration must support Block Kit payloads with `markdown` and `
|
|
122
|
+
- Your Slack integration must support Block Kit payloads with `markdown`, `table`, and the richer blocks emitted by this library (`rich_text`, `image`, and `divider`).
|
|
117
123
|
- This library does not help when your delivery path only accepts plain `text` or `mrkdwn` strings.
|
|
118
124
|
|
|
119
125
|
## Installation
|
|
@@ -210,8 +216,9 @@ Markdown segments with lines that contain only a non-breaking space. Those
|
|
|
210
216
|
placeholder lines are removed again when generating preview plain text, so Slack
|
|
211
217
|
notifications and logs stay close to the original Markdown source.
|
|
212
218
|
The current implementation deliberately skips blank runs that sit immediately
|
|
213
|
-
before setext-heading underlines or
|
|
214
|
-
boundaries can change Markdown meaning in newer
|
|
219
|
+
after list-item content, before setext-heading underlines, or before
|
|
220
|
+
reference-link definitions, because those boundaries can change Markdown meaning in newer
|
|
221
|
+
Slack Markdown rendering or keep list formatting open in some clients.
|
|
215
222
|
|
|
216
223
|
### Utility functions
|
|
217
224
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# slack-markdown-parser
|
|
2
2
|
|
|
3
|
-
LLM
|
|
3
|
+
LLM が生成する一般的な Markdown を、Slackで綺麗にレンダリングできるように、Slack Block Kit(`markdown` / `table` / 一部のリッチブロック)に変換する Python ライブラリです。
|
|
4
|
+
基本的な見出しや文字修飾は `markdown` ブロックで対応し、さらにテーブルや単純な単独リストなど、Slack ネイティブの表示が有効な記法は専用の Block Kit ブロックに変換することで、ChatGPT のような読み心地に近づけています。
|
|
4
5
|
|
|
5
6
|
## 背景
|
|
6
7
|
|
|
@@ -12,23 +13,25 @@ Slack で AI BOT を動かすとき、従来は Slack 独自の `mrkdwn` 形式
|
|
|
12
13
|
|
|
13
14
|
## 設計方針
|
|
14
15
|
|
|
15
|
-
Slack Block Kit
|
|
16
|
+
Slack Block Kit の比較的あたらしい `markdown` ブロック、`table` ブロック、そして安全に判定できる Markdown だけを対象にしたリッチブロック変換で、上の課題に対応できるようにしました。
|
|
16
17
|
|
|
17
18
|
| 課題 | 解決手段 |
|
|
18
19
|
|---|---|
|
|
19
|
-
| 変換の手間 | `markdown`
|
|
20
|
-
| 装飾の崩れ |
|
|
21
|
-
| テーブル非対応 | Markdown
|
|
20
|
+
| 変換の手間 | `markdown` ブロックがテーブル以外のある程度の Markdown をレンダリングできるので、LLM 出力を `mrkdwn` に書き換えずになるべくそのまま使う |
|
|
21
|
+
| 装飾の崩れ | `markdown` ブロックのMarkdownパーサーが完全ではないため、文字修飾が反映されない問題に対して、ゼロ幅スペース(`U+200B`)で装飾記号の境界を補い、それでも崩れる一部の日本語・中国語・韓国語のケースだけ可視スペースを使うことで、英語だけでなく、日本語、中国語、韓国語のレンダリングが崩れやすい言語についても、安定して装飾記号が反映される |
|
|
22
|
+
| テーブル非対応 | Markdown テーブルを検知して `table` ブロックに変換してレンダリングすると同時に、LLM が起こしがちな表の崩れ(パイプ不足等)も自動で補う |
|
|
23
|
+
| リッチなLLM出力 | 単独画像、水平線、単純な引用、コードフェンス、単純なリストを、意味が明確な範囲で Slack ネイティブの Block Kit ブロックへ変換する |
|
|
22
24
|
|
|
23
|
-
このライブラリの目標は、CommonMark や HTML を完全再現することではなく、Slack
|
|
25
|
+
このライブラリの目標は、CommonMark や HTML を完全再現することではなく、Slack 上で修飾記号を露出させず、自然に読める表示を作ることです。
|
|
24
26
|
Slack の `markdown` ブロック自体が対応していない構文は、古い `mrkdwn` へ無理に書き換えるより、安全なプレーンテキスト表示や `table` ブロック化を優先します。
|
|
25
27
|
|
|
26
28
|
## 主な機能
|
|
27
29
|
|
|
28
|
-
-
|
|
30
|
+
- 一般的な Markdown テキストを `markdown` ブロックに変換
|
|
29
31
|
- Markdown テーブルを `table` ブロックに変換
|
|
32
|
+
- 安全に判定できる単独 Markdown 構文を `image` / `divider` / `rich_text` ブロックに変換
|
|
30
33
|
- LLM が生成する表で起こりやすい崩れ(外枠パイプ不足、区切り行不足、列数不一致、空セル)を補正
|
|
31
|
-
-
|
|
34
|
+
- 必要に応じてメッセージを自動分割し、Slack の「1メッセージ1テーブル」制約とメッセージあたりのブロック数制限に対応
|
|
32
35
|
- ANSI escape / 制御文字を除去し、不正な Slack 角括弧トークンを無害化
|
|
33
36
|
- フェンスドコードブロック外では、装飾記号の前後にゼロ幅スペースを入れて表示崩れを減らす
|
|
34
37
|
- 日本語・中国語・韓国語の詰まった文で、インラインコードを含む装飾が崩れる一部のケースでは可視スペースを補って安定化
|
|
@@ -41,7 +44,7 @@ Slack の `markdown` ブロック自体が対応していない構文は、古
|
|
|
41
44
|
|
|
42
45
|
本ライブラリは、Slack の `markdown` / `table` ブロックが実際にどう見えるかを前提に設計しています。
|
|
43
46
|
|
|
44
|
-
Slack は 2026-03-06 に `markdown` ブロックの公式ドキュメントを更新し、見出し、水平線、タスクリスト、表、言語付きコードブロックなど、これまでより多くの Markdown
|
|
47
|
+
Slack は 2026-03-06 に `markdown` ブロックの公式ドキュメントを更新し、見出し、水平線、タスクリスト、表、言語付きコードブロックなど、これまでより多くの Markdown 記法をサポートし始めました。ただし、これらの機能は環境ごとに順番に有効化されるとも書かれています。つまり、ワークスペースやクライアントによって見え方が違う可能性があります。
|
|
45
48
|
|
|
46
49
|
現在の Slack で比較的安定して表示されるもの:
|
|
47
50
|
|
|
@@ -49,6 +52,7 @@ Slack は 2026-03-06 に `markdown` ブロックの公式ドキュメントを
|
|
|
49
52
|
- bare URL, `<https://...>` 形式リンク, Markdown リンク, 参照リンク, mailto リンク
|
|
50
53
|
- 箇条書き, 番号付きリスト, タスクリスト, 単純な引用
|
|
51
54
|
- Markdown テーブルを変換した明示的な Slack `table` ブロック
|
|
55
|
+
- 単独画像、コードフェンス、単純なリスト、単純な引用から生成したリッチブロック
|
|
52
56
|
- Slack 側で新しい Markdown 表示が有効な環境では、`markdown` ブロック内の見出し・水平線・表
|
|
53
57
|
|
|
54
58
|
Slack 側の制約として残るもの:
|
|
@@ -59,19 +63,22 @@ Slack 側の制約として残るもの:
|
|
|
59
63
|
- `markdown` ブロック内の生 Markdown テーブルは一部環境では表示されるが、安定性では明示的な Slack `table` ブロックの方が上
|
|
60
64
|
- Markdown 画像記法は `markdown` ブロック内では埋め込み画像にならない
|
|
61
65
|
- 数式, 生 HTML, HTML comment, `<details>`, admonition 記法, Mermaid は特別な表示にならず、テキストまたはコードとして出る
|
|
66
|
+
- 新しく公式記載された Block Kit ブロックでも、利用する `chat.postMessage` 経路では未対応の場合があるため、このライブラリは実際の Slack 送信で確認した保守的なリッチブロックだけを出力する
|
|
67
|
+
- Slack **モバイル**アプリは、`markdown` ブロック内のリスト項目に属する継続行(CommonMark で同じリスト項目に紐づくインデントされた段落)の先頭に、リストマーカーを再付加してしまう。例: `1. 見出し` の次にインデントされた継続段落を置くと、モバイルでは `1. 見出し` と `1.継続行...` のように番号が重複して表示される。Slack デスクトップ/Web は同じペイロードを正しく描画する。これはパーサー側の不具合ではなく Slack クライアントのレンダリング挙動。追跡: [issue #45](https://github.com/darkgaldragon/slack-markdown-parser/issues/45)。
|
|
62
68
|
|
|
63
69
|
このライブラリが吸収するもの:
|
|
64
70
|
|
|
65
71
|
- underscore 装飾 (`_..._`, `__...__`) を Slack 互換の asterisk 装飾へ正規化
|
|
66
72
|
- bare URL を Slack の `<https://...>` 形式にそろえる
|
|
67
73
|
- 崩れた Markdown テーブルを補って Slack `table` ブロックへ変換
|
|
74
|
+
- 意味が明確な単独 Markdown 構文を、raw `markdown` 表示に頼らず Slack ネイティブの Block Kit ブロックへ変換
|
|
68
75
|
- フェンスドコード内の table 風行をテーブル処理から除外
|
|
69
76
|
- 必要に応じて、内部空行を補助用の行に置き換えて段落の区切りを見えやすくする
|
|
70
77
|
- 生 HTML 風タグなど、Slack の特殊記法としては無効な `<...>` 形式を無害化
|
|
71
78
|
|
|
72
79
|
## 利用前提
|
|
73
80
|
|
|
74
|
-
- Slack Block Kit の `blocks` で `markdown` / `table`
|
|
81
|
+
- Slack Block Kit の `blocks` で `markdown` / `table` と、このライブラリが出力するリッチブロック(`rich_text`, `image`, `divider`)を送信できる実装が必要です。
|
|
75
82
|
- `text` / `mrkdwn` のみ送信できる経路では利用できません。
|
|
76
83
|
|
|
77
84
|
## インストール
|
|
@@ -161,7 +168,7 @@ QA | ~~保留~~ | Team C
|
|
|
161
168
|
|
|
162
169
|
`preserve_visual_blank_lines=True` は、非テーブル領域の内部空行を「改行を見えやすくするための補助行」に置き換えるオプションです。
|
|
163
170
|
この補助行はプレビュー文字列を作るときに取り除かれるので、通知文やログは元の Markdown に近い形を保てます。
|
|
164
|
-
|
|
171
|
+
また、リスト項目の内容直後、setext 見出しの下線直前、参照リンク定義直前には補助行を入れず、Markdown の意味が変わったり一部クライアントでリスト解釈が継続したりしないようにしています。
|
|
165
172
|
|
|
166
173
|
### ユーティリティ関数(公開関数)
|
|
167
174
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# slack-markdown-parser
|
|
2
2
|
|
|
3
|
-
`slack-markdown-parser` is a Python library that converts
|
|
3
|
+
`slack-markdown-parser` is a Python library that converts general Markdown generated by LLMs into Slack Block Kit messages built from `markdown`, `table`, and selected richer blocks so the output renders cleanly in Slack.
|
|
4
|
+
Basic headings and text formatting stay in `markdown` blocks, while constructs that benefit from dedicated Slack rendering, such as tables and simple standalone lists, are converted into native Block Kit blocks to produce a ChatGPT-like reading experience.
|
|
4
5
|
|
|
5
6
|
## Why this library exists
|
|
6
7
|
|
|
@@ -12,23 +13,25 @@ Many Slack AI bots have traditionally converted model output into Slack-specific
|
|
|
12
13
|
|
|
13
14
|
## Design approach
|
|
14
15
|
|
|
15
|
-
This library
|
|
16
|
+
This library combines Slack Block Kit's newer `markdown` block, `table` blocks, and richer block conversion for Markdown patterns that can be mapped safely.
|
|
16
17
|
|
|
17
18
|
| Problem | Approach |
|
|
18
19
|
|---|---|
|
|
19
|
-
| Extra conversion work |
|
|
20
|
-
| Formatting instability |
|
|
21
|
-
| No table syntax in `mrkdwn` | Detect Markdown tables and
|
|
20
|
+
| Extra conversion work | Slack `markdown` blocks can render a useful subset of Markdown other than tables, so LLM output can usually be sent with minimal rewriting instead of being converted into `mrkdwn`. |
|
|
21
|
+
| Formatting instability | Slack's `markdown` parser is not complete, so this library adds zero-width spaces (`U+200B`) around formatting-marker boundaries and uses visible spaces only for a few Japanese, Chinese, and Korean cases where that is still needed. This keeps emphasis stable across English and dense CJK text. |
|
|
22
|
+
| No table syntax in `mrkdwn` | Detect Markdown tables and render them as Slack `table` blocks, while also repairing common LLM-generated table issues such as missing pipes. |
|
|
23
|
+
| Rich LLM output | Convert standalone images, dividers, simple quotes, code fences, and simple lists into native Block Kit blocks where the Markdown structure is unambiguous. |
|
|
22
24
|
|
|
23
|
-
The goal is natural rendering on Slack, not full CommonMark or HTML fidelity.
|
|
25
|
+
The goal is natural rendering on Slack without exposing raw formatting markers, not full CommonMark or HTML fidelity.
|
|
24
26
|
If Slack itself does not support a construct in `markdown` blocks, this library prefers safe plain-text rendering or explicit `table` blocks over aggressive rewrites into old `mrkdwn`.
|
|
25
27
|
|
|
26
28
|
## Features
|
|
27
29
|
|
|
28
|
-
- Convert
|
|
30
|
+
- Convert general Markdown into Slack `markdown` blocks
|
|
29
31
|
- Convert Markdown tables into Slack `table` blocks
|
|
32
|
+
- Promote safe standalone Markdown constructs into richer Block Kit blocks: `image`, `divider`, and `rich_text`
|
|
30
33
|
- Repair common LLM table issues such as missing outer pipes, missing separator rows, mismatched column counts, and empty cells
|
|
31
|
-
- Split output into multiple Slack messages when needed to satisfy Slack's "one table per message"
|
|
34
|
+
- Split output into multiple Slack messages when needed to satisfy Slack's "one table per message" and per-message block-count constraints
|
|
32
35
|
- Remove ANSI/control characters and neutralize invalid Slack angle-bracket tokens before block generation
|
|
33
36
|
- Add zero-width spaces around inline formatting markers to reduce rendering issues outside fenced code blocks, while preserving English-like punctuation-only boundaries that Slack already renders reliably
|
|
34
37
|
- Add visible spaces for a small set of nested inline-code cases in dense Japanese, Chinese, and Korean text when zero-width spaces alone are not enough
|
|
@@ -43,7 +46,7 @@ The library is built around how Slack actually renders `markdown` and `table` bl
|
|
|
43
46
|
|
|
44
47
|
Slack updated its [`markdown` block docs](https://docs.slack.dev/reference/block-kit/blocks/markdown-block/)
|
|
45
48
|
and [changelog entry](https://docs.slack.dev/changelog/2026/03/06/block-kit-rich-text)
|
|
46
|
-
on March 6, 2026
|
|
49
|
+
on March 6, 2026 as it started supporting broader Markdown features such as headings, dividers, task lists,
|
|
47
50
|
native Markdown tables, and syntax-highlighted code blocks. In the Slack Web
|
|
48
51
|
workspace used for this project's April 8, 2026 validation, raw `markdown`
|
|
49
52
|
blocks rendered those constructs natively, including distinct heading levels
|
|
@@ -60,6 +63,7 @@ Reliable in current Slack rendering:
|
|
|
60
63
|
- Bare URLs, `<https://...>` style links, Markdown links, reference-style links, and mailto links
|
|
61
64
|
- Bullet lists, ordered lists, task lists, and simple blockquotes
|
|
62
65
|
- Explicit Slack `table` blocks generated from Markdown tables
|
|
66
|
+
- Explicit richer blocks generated from unambiguous Markdown, such as standalone images, code fences, simple lists, and simple quotes
|
|
63
67
|
- In environments where Slack has enabled the newer renderer, raw Markdown headings, dividers, and tables inside `markdown` blocks
|
|
64
68
|
|
|
65
69
|
Known Slack-side limitations:
|
|
@@ -70,19 +74,22 @@ Known Slack-side limitations:
|
|
|
70
74
|
- Raw Markdown tables inside `markdown` blocks now render in some newer Slack environments, but explicit Slack `table` blocks remain the reliable option across workspaces and delivery paths
|
|
71
75
|
- Markdown image syntax does not become an embedded image in `markdown` blocks
|
|
72
76
|
- Math, raw HTML, HTML comments, `<details>`, admonition syntax, and Mermaid are rendered as plain text or code, not as rich features
|
|
77
|
+
- Some newly documented Block Kit block types can be unavailable on a given `chat.postMessage` path; this library emits only a conservative subset of richer block types that has been validated through real Slack posting checks.
|
|
78
|
+
- The Slack **mobile** app re-prefixes the list marker onto each continuation line of a list item inside `markdown` blocks (e.g. `1. Heading` followed by an indented paragraph shows as `1. Heading` / `1.continuation` on mobile, while Slack desktop and Slack Web render the same payload correctly). This is a Slack client-side rendering behavior, not a parser bug. Tracking: [issue #45](https://github.com/darkgaldragon/slack-markdown-parser/issues/45).
|
|
73
79
|
|
|
74
80
|
What this library compensates for:
|
|
75
81
|
|
|
76
82
|
- Normalizes underscore emphasis (`_..._`, `__...__`) into Slack-friendly asterisk emphasis
|
|
77
83
|
- Wraps bare URLs into Slack-friendly `<https://...>` link form before sending `markdown` blocks
|
|
78
84
|
- Repairs malformed LLM-generated tables before converting them into Slack `table` blocks
|
|
85
|
+
- Converts unambiguous standalone Markdown constructs into native Block Kit blocks when that is safer than relying on raw `markdown` rendering
|
|
79
86
|
- Keeps table-like rows inside fenced code blocks out of table normalization
|
|
80
87
|
- Optionally turns internal blank lines into placeholder lines that keep paragraphs visibly separated in Slack `markdown` blocks
|
|
81
88
|
- Neutralizes invalid Slack angle-bracket tokens such as raw HTML-like tags
|
|
82
89
|
|
|
83
90
|
## Requirements
|
|
84
91
|
|
|
85
|
-
- Your Slack integration must support Block Kit payloads with `markdown` and `
|
|
92
|
+
- Your Slack integration must support Block Kit payloads with `markdown`, `table`, and the richer blocks emitted by this library (`rich_text`, `image`, and `divider`).
|
|
86
93
|
- This library does not help when your delivery path only accepts plain `text` or `mrkdwn` strings.
|
|
87
94
|
|
|
88
95
|
## Installation
|
|
@@ -179,8 +186,9 @@ Markdown segments with lines that contain only a non-breaking space. Those
|
|
|
179
186
|
placeholder lines are removed again when generating preview plain text, so Slack
|
|
180
187
|
notifications and logs stay close to the original Markdown source.
|
|
181
188
|
The current implementation deliberately skips blank runs that sit immediately
|
|
182
|
-
before setext-heading underlines or
|
|
183
|
-
boundaries can change Markdown meaning in newer
|
|
189
|
+
after list-item content, before setext-heading underlines, or before
|
|
190
|
+
reference-link definitions, because those boundaries can change Markdown meaning in newer
|
|
191
|
+
Slack Markdown rendering or keep list formatting open in some clients.
|
|
184
192
|
|
|
185
193
|
### Utility functions
|
|
186
194
|
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
|
|
10
10
|
## 出力
|
|
11
11
|
|
|
12
|
-
- Slack Block Kit ブロック(`markdown`
|
|
13
|
-
-
|
|
12
|
+
- Slack Block Kit ブロック(`markdown`, `table`, `rich_text`, `image`, `divider`)
|
|
13
|
+
- 複数テーブルや多数の昇格ブロックがある入力時は、「1メッセージ1テーブル」と Slack のメッセージあたりブロック数制限を満たすメッセージ群
|
|
14
14
|
|
|
15
15
|
## 設計目標
|
|
16
16
|
|
|
17
17
|
このパーサーは、CommonMark 全体や HTML / リッチドキュメントの完全再現を目標にはしません。
|
|
18
|
-
目的は、Slack Block Kit
|
|
18
|
+
目的は、Slack Block Kit で送ったときに自然に読めるメッセージを作ることです。
|
|
19
19
|
|
|
20
20
|
Markdown としての厳密さより Slack 上での読みやすさが重要になる場合は、Slack 上で安定して読める表現を優先します。
|
|
21
21
|
|
|
@@ -31,10 +31,10 @@ Markdown としての厳密さより Slack 上での読みやすさが重要に
|
|
|
31
31
|
6. テーブル領域と非テーブル領域に分割する
|
|
32
32
|
7. 領域ごとにブロックを作る
|
|
33
33
|
- テーブル領域: セル内装飾を解析して `table` ブロックを生成。変換に失敗した場合は `markdown` ブロックに戻す
|
|
34
|
-
- 非テーブル領域:
|
|
35
|
-
- `preserve_visual_blank_lines=True`
|
|
34
|
+
- 非テーブル領域: 安全に判定できる単独 Markdown 構文を先にリッチブロックへ変換し、残りのテキストは必要に応じてゼロ幅スペースを加えた上で `markdown` ブロックを生成する
|
|
35
|
+
- `preserve_visual_blank_lines=True` の場合は、残った `markdown` ブロックの内部空行を「ノーブレークスペースだけを含む行」に置き換えてから `markdown` ブロックを作る
|
|
36
36
|
|
|
37
|
-
`convert_markdown_to_slack_messages` は上記の結果を「1メッセージ1
|
|
37
|
+
`convert_markdown_to_slack_messages` は上記の結果を「1メッセージ1テーブル」制約と Slack のメッセージあたりブロック数制限に沿って分割します。
|
|
38
38
|
`convert_markdown_to_slack_payloads` は、同じ分割結果に `chat.postMessage.text` 用のプレビュー文字列を付けた送信データを返します。
|
|
39
39
|
|
|
40
40
|
## 実測ベースの Slack の挙動
|
|
@@ -49,6 +49,7 @@ Markdown としての厳密さより Slack 上での読みやすさが重要に
|
|
|
49
49
|
- bare URL, `<https://...>` 形式リンク, Markdown リンク, 参照リンク, mailto リンク
|
|
50
50
|
- 箇条書き, 番号付きリスト, タスクリスト, 単純な1段引用
|
|
51
51
|
- Slack `table` ブロック
|
|
52
|
+
- 意味が明確な単独 Markdown 構文から生成した Slack ネイティブのリッチブロック
|
|
52
53
|
|
|
53
54
|
Slack は 2026-03-06 に `markdown` ブロックの公式ドキュメントを更新し、これまでより多くの Markdown 記法を案内し始めました。本プロジェクトの 2026-04-08 の Slack Web 実測では、raw の `markdown` ブロックで次を確認しています。
|
|
54
55
|
|
|
@@ -73,6 +74,13 @@ Slack は 2026-03-06 に `markdown` ブロックの公式ドキュメントを
|
|
|
73
74
|
- `_..._` / `__...__` を Slack 互換の `*...*` / `**...**` に正規化する
|
|
74
75
|
- bare URL を Slack で安定しやすい `<https://...>` 形式にそろえる
|
|
75
76
|
- 崩れた Markdown テーブルを補って `table` ブロックへ変換する
|
|
77
|
+
- 意味が明確な単独 Markdown 構文を Slack ネイティブのブロックへ変換する
|
|
78
|
+
- 単独行の画像構文 `` → `image`
|
|
79
|
+
- 水平線 → `divider`
|
|
80
|
+
- フェンスドコード → `rich_text_preformatted`
|
|
81
|
+
- 単純な1段引用 → `rich_text_quote`
|
|
82
|
+
- 単純な箇条書き / 番号付きリスト → `rich_text_list`
|
|
83
|
+
- リストは、テキスト領域の先頭または空行の直後から始まり、連続する非空行がすべてリスト項目で、1〜3スペースの曖昧なネストインデントや Markdown バックスラッシュエスケープに依存せず、直後にインデント付きの継続段落がない場合だけ昇格する
|
|
76
84
|
- フェンスドコード内の table 風行をテーブル解析対象から除外する
|
|
77
85
|
- 内部空行を、必要に応じて段落区切りを見えやすくする補助行へ置き換える
|
|
78
86
|
- `<foo>` や生 HTML 風タグのような、Slack の特殊記法としては無効な `<...>` 形式を無害化する
|
|
@@ -205,6 +213,7 @@ LLM は外枠パイプの省略、区切り行の欠落、列数の不一致な
|
|
|
205
213
|
- 見える行に挟まれた内部空行だけを書き換える
|
|
206
214
|
- 先頭・末尾の空行のまとまりはそのまま残す
|
|
207
215
|
- table 領域には適用しない
|
|
216
|
+
- リスト項目の内容直後の空行はそのまま残す
|
|
208
217
|
- setext 見出しの下線直前の空行はそのまま残す
|
|
209
218
|
- 参照リンク定義直前の空行はそのまま残す
|
|
210
219
|
- プレビュー文字列と `blocks_to_plain_text` では補助用のマーカーを除去し、元の空行構造に戻す
|
|
@@ -9,13 +9,13 @@ This document describes how `slack-markdown-parser` converts Markdown into Slack
|
|
|
9
9
|
|
|
10
10
|
## Output
|
|
11
11
|
|
|
12
|
-
- Slack Block Kit blocks (`markdown`
|
|
13
|
-
- When the input contains multiple tables, a list of messages that satisfies the "one table per message" rule
|
|
12
|
+
- Slack Block Kit blocks (`markdown`, `table`, `rich_text`, `image`, and `divider`)
|
|
13
|
+
- When the input contains multiple tables or many promoted blocks, a list of messages that satisfies the "one table per message" rule and Slack's per-message block-count limit
|
|
14
14
|
|
|
15
15
|
## Design target
|
|
16
16
|
|
|
17
17
|
This parser does not try to reproduce full CommonMark, HTML, or rich-document rendering.
|
|
18
|
-
Its goal is to produce Slack messages that read naturally when delivered through Slack Block Kit
|
|
18
|
+
Its goal is to produce Slack messages that read naturally when delivered through Slack Block Kit blocks.
|
|
19
19
|
|
|
20
20
|
When exact Markdown fidelity conflicts with Slack readability, readable Slack output takes priority.
|
|
21
21
|
|
|
@@ -31,10 +31,10 @@ When exact Markdown fidelity conflicts with Slack readability, readable Slack ou
|
|
|
31
31
|
6. Split the text into table regions and non-table regions
|
|
32
32
|
7. Build blocks for each region:
|
|
33
33
|
- Table regions: parse inline cell styling and generate a `table` block. If conversion fails, such as when there are fewer than two candidate lines or the parse result is empty, fall back to a `markdown` block.
|
|
34
|
-
- Non-table regions: add zero-width spaces where needed and generate
|
|
35
|
-
- If `preserve_visual_blank_lines=True`, replace internal blank lines in
|
|
34
|
+
- Non-table regions: first promote safe standalone Markdown constructs into richer Block Kit blocks, then add zero-width spaces where needed and generate `markdown` blocks for the remaining text.
|
|
35
|
+
- If `preserve_visual_blank_lines=True`, replace internal blank lines in remaining `markdown` blocks with lines that contain only a non-breaking space before emitting the `markdown` block.
|
|
36
36
|
|
|
37
|
-
`convert_markdown_to_slack_messages` then splits the resulting block list to satisfy the "one table per message" rule.
|
|
37
|
+
`convert_markdown_to_slack_messages` then splits the resulting block list to satisfy the "one table per message" rule and Slack's per-message block-count limit.
|
|
38
38
|
`convert_markdown_to_slack_payloads` returns the same split blocks plus preview `text` values ready for `chat.postMessage`.
|
|
39
39
|
|
|
40
40
|
## How Slack behaved in testing
|
|
@@ -49,6 +49,7 @@ The behaviors below are based on practical validation against real Slack clients
|
|
|
49
49
|
- Bare URLs, `<https://...>` style links, Markdown links, reference-style links, and mailto links
|
|
50
50
|
- Bullet lists, ordered lists, task lists, and simple one-level blockquotes
|
|
51
51
|
- Slack `table` blocks
|
|
52
|
+
- Native richer blocks generated from unambiguous standalone Markdown constructs
|
|
52
53
|
|
|
53
54
|
Slack published updated `markdown` block documentation and a changelog entry on March 6, 2026. In the Slack Web workspace validated for this project on April 8, 2026, raw `markdown` blocks rendered:
|
|
54
55
|
|
|
@@ -73,6 +74,13 @@ Slack still controls when those newer features appear and how they look, so trea
|
|
|
73
74
|
- `_..._` and `__...__` are normalized into Slack-friendly `*...*` and `**...**`
|
|
74
75
|
- Bare URLs are wrapped into Slack-friendly `<https://...>` form before `markdown` block delivery
|
|
75
76
|
- Malformed Markdown tables are repaired before `table` block generation
|
|
77
|
+
- Unambiguous standalone Markdown constructs are promoted into native Slack blocks:
|
|
78
|
+
- standalone image syntax `` to `image`
|
|
79
|
+
- thematic-break lines to `divider`
|
|
80
|
+
- fenced code blocks to `rich_text_preformatted`
|
|
81
|
+
- simple one-level quotes to `rich_text_quote`
|
|
82
|
+
- simple bullet and ordered lists to `rich_text_list`
|
|
83
|
+
- Lists are promoted only when the list starts at the beginning of the text region or after a blank line, each non-blank line in the run is a list item, the list does not use ambiguous 1-3-space nested indentation, the item text does not rely on Markdown backslash escapes, and the run is not followed by an indented continuation paragraph.
|
|
76
84
|
- Table-like rows inside fenced code blocks are kept out of table parsing
|
|
77
85
|
- Internal blank lines can optionally be rewritten into placeholder lines so Slack keeps visible paragraph separation
|
|
78
86
|
- Unsupported Slack angle-bracket tokens such as `<foo>` or raw HTML-like tags are neutralized
|
|
@@ -207,6 +215,7 @@ This workaround is intentionally narrow:
|
|
|
207
215
|
- Only blank lines between visible lines are rewritten
|
|
208
216
|
- Leading and trailing blank runs are left untouched
|
|
209
217
|
- Table segments are not modified by this option
|
|
218
|
+
- Blank runs immediately after list-item content are left untouched
|
|
210
219
|
- Blank runs immediately before setext-heading underlines are left untouched
|
|
211
220
|
- Blank runs immediately before reference-link definitions are left untouched
|
|
212
221
|
- Preview text and `blocks_to_plain_text` remove those placeholder markers again, preserving the original blank lines in plain-text output
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "slack-markdown-parser"
|
|
7
|
-
version = "2.
|
|
8
|
-
description = "Convert LLM Markdown into Slack Block Kit
|
|
7
|
+
version = "2.4.0"
|
|
8
|
+
description = "Convert LLM Markdown into Slack Block Kit messages"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = "MIT"
|
|
@@ -33,7 +33,6 @@ dev = [
|
|
|
33
33
|
"pytest>=8.0.0",
|
|
34
34
|
"pytest-cov>=5.0.0",
|
|
35
35
|
"ruff>=0.6.0",
|
|
36
|
-
"twine>=5.1.0",
|
|
37
36
|
]
|
|
38
37
|
|
|
39
38
|
[project.urls]
|
|
@@ -57,4 +56,7 @@ target-version = "py310"
|
|
|
57
56
|
|
|
58
57
|
[tool.ruff.lint]
|
|
59
58
|
select = ["E", "F", "I", "UP", "B"]
|
|
60
|
-
ignore = ["E501"
|
|
59
|
+
ignore = ["E501"] # line length is enforced by black
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
testpaths = ["tests"]
|