slack-markdown-parser 2.2.1__tar.gz → 2.2.2__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.
Files changed (30) hide show
  1. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/CHANGELOG.md +11 -4
  2. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/PKG-INFO +37 -3
  3. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/README-ja.md +37 -2
  4. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/README.md +36 -2
  5. slack_markdown_parser-2.2.2/docs/slack-client-manual-checklist.md +53 -0
  6. slack_markdown_parser-2.2.2/docs/slack-nested-modifier-findings.md +59 -0
  7. slack_markdown_parser-2.2.2/docs/slack-render-test-workflow.md +117 -0
  8. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/docs/spec-ja.md +43 -1
  9. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/docs/spec.md +43 -1
  10. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/pyproject.toml +1 -1
  11. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/slack_markdown_parser/__init__.py +1 -1
  12. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/slack_markdown_parser/converter.py +363 -25
  13. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/slack_markdown_parser.egg-info/PKG-INFO +37 -3
  14. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/slack_markdown_parser.egg-info/SOURCES.txt +9 -1
  15. slack_markdown_parser-2.2.2/tests/fixtures/slack_cjk_inner_code_matrix.md +275 -0
  16. slack_markdown_parser-2.2.2/tests/fixtures/slack_nested_modifier_matrix.md +365 -0
  17. slack_markdown_parser-2.2.2/tests/fixtures/slack_nested_modifier_matrix_parens.md +365 -0
  18. slack_markdown_parser-2.2.2/tests/fixtures/slack_nested_modifier_matrix_quotes.md +365 -0
  19. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/tests/test_converter.py +235 -0
  20. slack_markdown_parser-2.2.2/tests/test_nested_modifier_matrix.py +86 -0
  21. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/CONTRIBUTING.md +0 -0
  22. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/LICENSE +0 -0
  23. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/MANIFEST.in +0 -0
  24. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/setup.cfg +0 -0
  25. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/slack_markdown_parser/py.typed +0 -0
  26. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/slack_markdown_parser.egg-info/dependency_links.txt +0 -0
  27. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/slack_markdown_parser.egg-info/requires.txt +0 -0
  28. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/slack_markdown_parser.egg-info/top_level.txt +0 -0
  29. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/tests/fixtures/llm_markdown_p0_corpus.md +0 -0
  30. {slack_markdown_parser-2.2.1 → slack_markdown_parser-2.2.2}/tests/test_llm_markdown_p0_corpus.py +0 -0
@@ -6,15 +6,22 @@ The format is based on Keep a Changelog, and the project follows Semantic Versio
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.2.2] - 2026-03-10
10
+
9
11
  ### Added
10
12
 
11
- - Added a contributor guide, issue templates, and a pull request template.
12
- - Added the `py.typed` marker and packaging rules for docs and tests in source distributions.
13
+ - Added Slack render-test tooling, generated regression fixtures, and maintainer docs for validating real Slack client rendering across Web, desktop, and mobile.
13
14
 
14
15
  ### Changed
15
16
 
16
- - CI now runs the test suite on Python 3.10, 3.11, 3.12, and 3.13.
17
- - `.gitignore` no longer ignores the entire `tests/` directory, so new tests and sample fixtures can be tracked.
17
+ - Refreshed the README and behavior spec to distinguish Slack renderer limitations from parser-owned normalization and repair behavior.
18
+ - Clarified locale-aware formatting behavior and added public examples for the Slack render-test workflow.
19
+
20
+ ### Fixed
21
+
22
+ - Stabilized nested inline-code emphasis rendering across English, Japanese, Chinese, and Korean contexts, including existing-ZWSP boundaries and CJK italic/strike cases.
23
+ - Preserved user-authored spacing in fallback text while removing parser-inserted rendering-only padding.
24
+ - Wrapped bare URLs into Slack-friendly autolink form before sending `markdown` blocks so adjacent lines no longer collapse into malformed angle-link text.
18
25
 
19
26
  ## [2.2.1] - 2026-03-07
20
27
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slack-markdown-parser
3
- Version: 2.2.1
3
+ Version: 2.2.2
4
4
  Summary: Convert LLM Markdown into Slack Block Kit markdown/table messages
5
5
  Author: darkgaldragon
6
6
  License-Expression: MIT
@@ -48,9 +48,12 @@ This library leans on Slack Block Kit's `markdown` block for standard Markdown a
48
48
  | Problem | Approach |
49
49
  |---|---|
50
50
  | Conversion overhead | Send standard Markdown through Slack `markdown` blocks without rewriting it into `mrkdwn`. |
51
- | Formatting instability | Insert zero-width spaces (ZWSP, U+200B) around formatting tokens when needed so Slack parses inline styling more reliably without visible extra spaces. |
51
+ | Formatting instability | Prefer zero-width spaces (ZWSP, U+200B) around formatting tokens, and fall back to locale-aware visible-space padding for CJK nested inline-code cases where Slack rendering still breaks. |
52
52
  | No table syntax in `mrkdwn` | Detect Markdown tables and convert them into Slack `table` blocks, including repair of common LLM-generated table inconsistencies. |
53
53
 
54
+ The target is natural rendering on Slack, not full CommonMark or HTML fidelity.
55
+ 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
+
54
57
  ## Features
55
58
 
56
59
  - Convert standard Markdown into Slack `markdown` blocks
@@ -59,10 +62,38 @@ This library leans on Slack Block Kit's `markdown` block for standard Markdown a
59
62
  - Split output into multiple Slack messages when needed to satisfy Slack's "one table per message" constraint
60
63
  - Sanitize ANSI/control characters and neutralize invalid Slack angle-bracket tokens before block generation
61
64
  - Add ZWSP around inline formatting tokens to reduce rendering issues outside fenced code blocks
65
+ - Use locale-aware visible-space padding for nested inline-code emphasis in dense Japanese, Chinese, and Korean text when Slack requires stronger boundaries than ZWSP alone
62
66
  - Support Markdown and Slack-style links inside table cells
63
- - Build fallback text for `chat.postMessage.text` from generated blocks
67
+ - Build fallback text for `chat.postMessage.text` from generated blocks, normalizing synthetic ZWSP and any parser-inserted visible-space padding used only for rendering stability
64
68
  - Accept raw LLM Markdown without tightly constraining the model prompt, using best-effort sanitize and table repair before Slack delivery
65
69
 
70
+ ## Observed Slack behavior
71
+
72
+ The library is built around how Slack actually renders `markdown` and `table` blocks in practice.
73
+
74
+ Reliable in current Slack rendering:
75
+
76
+ - `**bold**`, `*italic*`, `~~strike~~`, inline code, and fenced code blocks
77
+ - Bare URLs, autolinks, Markdown links, reference-style links, and mailto links
78
+ - Bullet lists, ordered lists, task lists, and simple blockquotes
79
+ - Explicit Slack `table` blocks generated from Markdown tables
80
+
81
+ Known Slack-side limitations:
82
+
83
+ - Heading syntax (`#`, setext headings) renders as plain text rather than true heading levels
84
+ - Nested blockquotes are weak compared with full Markdown renderers
85
+ - Horizontal rules render more like line text than semantic separators
86
+ - Markdown image syntax does not become an embedded image in `markdown` blocks
87
+ - Math, raw HTML, HTML comments, `<details>`, admonition syntax, and Mermaid are rendered as plain text or code, not as rich features
88
+
89
+ What this library compensates for:
90
+
91
+ - Normalizes underscore emphasis (`_..._`, `__...__`) into Slack-friendly asterisk emphasis
92
+ - Wraps bare URLs into Slack-friendly autolink form before sending `markdown` blocks
93
+ - Repairs malformed LLM-generated tables before converting them into Slack `table` blocks
94
+ - Keeps table-like rows inside fenced code blocks out of table normalization
95
+ - Neutralizes invalid Slack angle-bracket tokens such as raw HTML-like tags
96
+
66
97
  ## Requirements
67
98
 
68
99
  - Your Slack integration must support Block Kit payloads with `markdown` and `table` blocks.
@@ -175,6 +206,9 @@ These are also part of the public package surface:
175
206
 
176
207
  - Behavior spec: [docs/spec.md](docs/spec.md)
177
208
  - Japanese behavior spec: [docs/spec-ja.md](docs/spec-ja.md)
209
+ - Slack render-test workflow: [docs/slack-render-test-workflow.md](docs/slack-render-test-workflow.md)
210
+ - Nested-modifier findings: [docs/slack-nested-modifier-findings.md](docs/slack-nested-modifier-findings.md)
211
+ - Desktop/mobile manual checklist: [docs/slack-client-manual-checklist.md](docs/slack-client-manual-checklist.md)
178
212
  - Non-goals:
179
213
  - Generating Slack `mrkdwn` strings
180
214
  - Supporting clients or MCP tools that can only send `mrkdwn`
@@ -17,9 +17,12 @@ Slack Block Kit の `markdown` ブロック(標準 Markdown 構文をそのま
17
17
  | 課題 | 解決手段 |
18
18
  |---|---|
19
19
  | 変換コスト | `markdown` ブロックが標準 Markdown をそのまま受け付けるため、LLM 出力を変換せず利用可能 |
20
- | 装飾崩れ | 装飾記号の前後に ZWSP(ゼロ幅スペース U+200B)を自動挿入してレンダリングを安定化。通常の半角スペースではなくゼロ幅スペースを使うことで、見た目上の不自然な空白を生じさせずに Slack の装飾解析を補助する |
20
+ | 装飾崩れ | 基本は ZWSP(ゼロ幅スペース U+200B)で装飾境界を補強し、CJK の密着文脈でインラインコードを内包する装飾が崩れる場合のみ、言語別ルールで可視スペースも使ってレンダリングを安定化する |
21
21
  | テーブル非対応 | Markdown テーブルを検知して `table` ブロックに変換。LLM の出力する多様なテーブル記法の揺れも自動補完し `invalid_blocks` エラーを回避 |
22
22
 
23
+ このライブラリの目標は、CommonMark や HTML を完全再現することではなく、Slack 上で自然に読める表示を作ることです。
24
+ Slack の `markdown` ブロック自体が対応していない構文は、古い `mrkdwn` へ無理に書き換えるより、安全なプレーンテキスト表示や `table` ブロック化を優先します。
25
+
23
26
  ## 主な機能
24
27
 
25
28
  - 標準 Markdown テキストを `markdown` ブロックに変換
@@ -28,10 +31,38 @@ Slack Block Kit の `markdown` ブロック(標準 Markdown 構文をそのま
28
31
  - テーブルごとにメッセージを自動分割(Slack の「1メッセージ1テーブル」制約に対応)
29
32
  - ANSI escape / 制御文字を除去し、不正な Slack 角括弧トークンを自動で無害化
30
33
  - 装飾記号の前後に ZWSP を付与して表示崩れを抑制(フェンスドコードブロック内は除外、インラインコードは付与対象)
34
+ - 日本語・中国語・韓国語の密着文脈で、インラインコードを内包する装飾には可視スペースを補って Slack 表示を安定化
31
35
  - テーブルセル内の Markdown link / Slack link を認識
32
- - `chat.postMessage.text` 用の fallback テキストを生成(ブロック内容をプレーンテキスト化して通知プレビュー等に利用)
36
+ - `chat.postMessage.text` 用の fallback テキストを生成(表示安定化のために入れた ZWSP や人工的な可視スペースは通知文では自然な形に正規化)
33
37
  - モデル側で Markdown を厳密に制御しなくてもよいよう、Slack 送信前にベストエフォートでサニタイズとテーブル補完を行う
34
38
 
39
+ ## 実測ベースの Slack 挙動
40
+
41
+ 本ライブラリは、Slack の `markdown` / `table` ブロックが実際にどう見えるかを前提に設計しています。
42
+
43
+ 現在の Slack で安定して表示されるもの:
44
+
45
+ - `**bold**`, `*italic*`, `~~strike~~`, インラインコード, フェンスドコード
46
+ - bare URL, autolink, Markdown link, 参照リンク, mailto link
47
+ - 箇条書き, 番号付きリスト, タスクリスト, 単純な引用
48
+ - Markdown テーブルを変換した明示的な Slack `table` ブロック
49
+
50
+ Slack 側の制約として残るもの:
51
+
52
+ - `#` 見出しや setext 見出しは、真の見出しレベルではなくプレーンテキスト寄りに表示される
53
+ - 多段引用はフル Markdown レンダラほどきれいに出ない
54
+ - 水平線は semantic な区切りではなく線テキスト寄りに見える
55
+ - Markdown 画像記法は `markdown` ブロック内では埋め込み画像にならない
56
+ - 数式, 生 HTML, HTML comment, `<details>`, admonition 記法, Mermaid はリッチ機能としては扱われず、テキストまたはコードとして表示される
57
+
58
+ 本ライブラリが吸収するもの:
59
+
60
+ - underscore 装飾 (`_..._`, `__...__`) を Slack 互換の asterisk 装飾へ正規化
61
+ - bare URL を Slack の `markdown` ブロックで安定する autolink 形式へ正規化
62
+ - LLM が崩したテーブル記法を補完して Slack `table` ブロックへ変換
63
+ - フェンスドコード内の table 風行をテーブル正規化対象から除外
64
+ - 生 HTML 風タグなど、不正な Slack angle token を無害化
65
+
35
66
  ## 利用前提
36
67
 
37
68
  - Slack Block Kit の `blocks` で `markdown` / `table` ブロックを送信できる実装が必要です。
@@ -131,6 +162,10 @@ QA | ~~保留~~ | Team C
131
162
  ## 仕様
132
163
 
133
164
  - 挙動仕様: [docs/spec-ja.md](docs/spec-ja.md)
165
+ - 英語仕様: [docs/spec.md](docs/spec.md)
166
+ - Slack 実レンダリング検証手順: [docs/slack-render-test-workflow.md](docs/slack-render-test-workflow.md)
167
+ - nested modifier 実測メモ: [docs/slack-nested-modifier-findings.md](docs/slack-nested-modifier-findings.md)
168
+ - Desktop / mobile 手動確認: [docs/slack-client-manual-checklist.md](docs/slack-client-manual-checklist.md)
134
169
  - 非対応:
135
170
  - `mrkdwn` 文字列の生成
136
171
  - `mrkdwn` のみ送信可能なクライアント/MCP ツール
@@ -17,9 +17,12 @@ This library leans on Slack Block Kit's `markdown` block for standard Markdown a
17
17
  | Problem | Approach |
18
18
  |---|---|
19
19
  | Conversion overhead | Send standard Markdown through Slack `markdown` blocks without rewriting it into `mrkdwn`. |
20
- | Formatting instability | Insert zero-width spaces (ZWSP, U+200B) around formatting tokens when needed so Slack parses inline styling more reliably without visible extra spaces. |
20
+ | Formatting instability | Prefer zero-width spaces (ZWSP, U+200B) around formatting tokens, and fall back to locale-aware visible-space padding for CJK nested inline-code cases where Slack rendering still breaks. |
21
21
  | No table syntax in `mrkdwn` | Detect Markdown tables and convert them into Slack `table` blocks, including repair of common LLM-generated table inconsistencies. |
22
22
 
23
+ The target is natural rendering on Slack, not full CommonMark or HTML fidelity.
24
+ 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
+
23
26
  ## Features
24
27
 
25
28
  - Convert standard Markdown into Slack `markdown` blocks
@@ -28,10 +31,38 @@ This library leans on Slack Block Kit's `markdown` block for standard Markdown a
28
31
  - Split output into multiple Slack messages when needed to satisfy Slack's "one table per message" constraint
29
32
  - Sanitize ANSI/control characters and neutralize invalid Slack angle-bracket tokens before block generation
30
33
  - Add ZWSP around inline formatting tokens to reduce rendering issues outside fenced code blocks
34
+ - Use locale-aware visible-space padding for nested inline-code emphasis in dense Japanese, Chinese, and Korean text when Slack requires stronger boundaries than ZWSP alone
31
35
  - Support Markdown and Slack-style links inside table cells
32
- - Build fallback text for `chat.postMessage.text` from generated blocks
36
+ - Build fallback text for `chat.postMessage.text` from generated blocks, normalizing synthetic ZWSP and any parser-inserted visible-space padding used only for rendering stability
33
37
  - Accept raw LLM Markdown without tightly constraining the model prompt, using best-effort sanitize and table repair before Slack delivery
34
38
 
39
+ ## Observed Slack behavior
40
+
41
+ The library is built around how Slack actually renders `markdown` and `table` blocks in practice.
42
+
43
+ Reliable in current Slack rendering:
44
+
45
+ - `**bold**`, `*italic*`, `~~strike~~`, inline code, and fenced code blocks
46
+ - Bare URLs, autolinks, Markdown links, reference-style links, and mailto links
47
+ - Bullet lists, ordered lists, task lists, and simple blockquotes
48
+ - Explicit Slack `table` blocks generated from Markdown tables
49
+
50
+ Known Slack-side limitations:
51
+
52
+ - Heading syntax (`#`, setext headings) renders as plain text rather than true heading levels
53
+ - Nested blockquotes are weak compared with full Markdown renderers
54
+ - Horizontal rules render more like line text than semantic separators
55
+ - Markdown image syntax does not become an embedded image in `markdown` blocks
56
+ - Math, raw HTML, HTML comments, `<details>`, admonition syntax, and Mermaid are rendered as plain text or code, not as rich features
57
+
58
+ What this library compensates for:
59
+
60
+ - Normalizes underscore emphasis (`_..._`, `__...__`) into Slack-friendly asterisk emphasis
61
+ - Wraps bare URLs into Slack-friendly autolink form before sending `markdown` blocks
62
+ - Repairs malformed LLM-generated tables before converting them into Slack `table` blocks
63
+ - Keeps table-like rows inside fenced code blocks out of table normalization
64
+ - Neutralizes invalid Slack angle-bracket tokens such as raw HTML-like tags
65
+
35
66
  ## Requirements
36
67
 
37
68
  - Your Slack integration must support Block Kit payloads with `markdown` and `table` blocks.
@@ -144,6 +175,9 @@ These are also part of the public package surface:
144
175
 
145
176
  - Behavior spec: [docs/spec.md](docs/spec.md)
146
177
  - Japanese behavior spec: [docs/spec-ja.md](docs/spec-ja.md)
178
+ - Slack render-test workflow: [docs/slack-render-test-workflow.md](docs/slack-render-test-workflow.md)
179
+ - Nested-modifier findings: [docs/slack-nested-modifier-findings.md](docs/slack-nested-modifier-findings.md)
180
+ - Desktop/mobile manual checklist: [docs/slack-client-manual-checklist.md](docs/slack-client-manual-checklist.md)
147
181
  - Non-goals:
148
182
  - Generating Slack `mrkdwn` strings
149
183
  - Supporting clients or MCP tools that can only send `mrkdwn`
@@ -0,0 +1,53 @@
1
+ # Slack Client Manual Checklist
2
+
3
+ Use this for quick smoke checks in Slack Desktop and mobile after parser changes.
4
+
5
+ ## How to run
6
+
7
+ 1. Open the link for each case in Slack Desktop or mobile.
8
+ 2. Confirm the expected visible text and formatting.
9
+ 3. Mark `PASS` only if all checks below hold.
10
+
11
+ ## Pass criteria
12
+
13
+ - Outer emphasis is rendered for the whole target span.
14
+ - Inline code remains monospace.
15
+ - Raw markers such as `**`, `*`, or `~~` are not visible.
16
+ - English cases do not show extra visible spaces before punctuation.
17
+ - Japanese/Chinese/Korean nested-code cases may show visible spaces around the emphasized span when needed; that is expected.
18
+
19
+ ## Cases
20
+
21
+ - `manual_en`
22
+ Expected: `Frontend (App.tsx)` is bold, `App.tsx` is code, no extra space before `:`
23
+ Link: paste your `manual_en` permalink here
24
+
25
+ - `manual_ja`
26
+ Expected: `フロントエンド (App.tsx)` is bold, `App.tsx` is code, visible spaces around the bold span are acceptable
27
+ Link: paste your `manual_ja` permalink here
28
+
29
+ - `manual_zh`
30
+ Expected: `外侧(内侧)` span is bold, inner code remains code, visible spaces around the bold span are acceptable
31
+ Link: paste your `manual_zh` permalink here
32
+
33
+ - `manual_ko`
34
+ Expected: `바깥(내부)강조` span is bold, inner code remains code, trailing visible space before `입니다.` is acceptable
35
+ Link: paste your `manual_ko` permalink here
36
+
37
+ - `manual_mix_ja_en`
38
+ Expected: `Frontend (App.tsx)` is bold inside Japanese text, `App.tsx` is code
39
+ Link: paste your `manual_mix_ja_en` permalink here
40
+
41
+ - `manual_mix_ko_en`
42
+ Expected: `Frontend (App.tsx)` is bold inside Korean text, `App.tsx` is code
43
+ Link: paste your `manual_mix_ko_en` permalink here
44
+
45
+ - `manual_mix_en_ja`
46
+ Expected: `機能A (ID-1)` is bold inside English text, `ID-1` is code, no extra visible spaces are introduced
47
+ Link: paste your `manual_mix_en_ja` permalink here
48
+
49
+ ## Recording
50
+
51
+ - `PASS`: rendering matches the expectation above
52
+ - `FAIL`: capture which marker leaked or which style was missing
53
+ - If Desktop and mobile differ, record the client and version separately
@@ -0,0 +1,59 @@
1
+ # Slack Nested Modifier Findings
2
+
3
+ Observed on Slack Web against `#bot-test` on 2026-03-09.
4
+
5
+ ## Variants
6
+
7
+ - `plain`: inner content without surrounding punctuation
8
+ - `parens`: inner content wrapped in parentheses
9
+ - `quotes`: inner content wrapped in quotes
10
+
11
+ ## Source fixtures
12
+
13
+ - `tests/fixtures/slack_nested_modifier_matrix.md`
14
+ - `tests/fixtures/slack_nested_modifier_matrix_parens.md`
15
+ - `tests/fixtures/slack_nested_modifier_matrix_quotes.md`
16
+ - `tests/fixtures/slack_cjk_inner_code_matrix.md`
17
+
18
+ ## Note on private artifacts
19
+
20
+ The concrete Slack permalinks used during validation are intentionally omitted from the public repository. Re-run the workflow in `docs/slack-render-test-workflow.md` inside your own workspace if you want message-level links for manual verification.
21
+
22
+ ## Shared result before locale-aware fallback
23
+
24
+ Before the locale-aware visible-space fallback was introduced, all three content variants produced the same high-level outcome:
25
+
26
+ - English boundaries succeeded in `raw` and `parser`
27
+ - Japanese `spaces_both` succeeded in `raw` and `parser`
28
+ - Japanese `outer_zwsp` succeeded in `raw` and `parser`
29
+ - Japanese `tight`, `space_left`, and `space_right` failed in `raw`
30
+ - `parser` improved those Japanese boundary buckets from `0/5` to `4/5`
31
+ - The remaining failures were concentrated in `inner_code`
32
+
33
+ ## Focused CJK result
34
+
35
+ Fixture:
36
+
37
+ - `tests/fixtures/slack_cjk_inner_code_matrix.md`
38
+
39
+ This focused matrix contained `ja`, `zh`, and `ko`, testing only:
40
+
41
+ - inner `plain`
42
+ - inner `code`
43
+
44
+ Boundary behavior after improvement:
45
+
46
+ - `ja`, `zh`, and `ko` all still succeeded for `outer_zwsp`
47
+ - `ja`, `zh`, and `ko` all still succeeded for `spaces_both`
48
+ - raw `tight` and raw `space_left` remained fragile across all three locales
49
+ - raw `space_right` remained fragile for `ja` and `zh`, but was already stable for `ko`
50
+ - after the locale-aware fallback, `parser` reached `2/2` success in every tested bucket for `ja`, `zh`, and `ko`
51
+
52
+ Current takeaway:
53
+
54
+ - Treating Chinese like Japanese is reasonable based on current Slack Web behavior.
55
+ - Korean still differs at the raw-rendering level, but the parser can now normalize all tested Korean buckets successfully with a lighter right-side spacing rule.
56
+ - The current parser strategy for nested inline code is:
57
+ - `ja` / `zh`: add visible spaces on whichever outer side is missing
58
+ - `ko`: ensure a visible trailing space when needed
59
+ - English-like boundaries: preserve the original outer formatting span
@@ -0,0 +1,117 @@
1
+ # Slack Render Test Workflow
2
+
3
+ This repository includes a minimal local workflow for validating how Slack
4
+ actually renders generated Block Kit `markdown` and `table` output.
5
+
6
+ ## Files
7
+
8
+ - `docs/slack-render-test-app-manifest.yaml`
9
+ - Minimal Slack App manifest for a test-only bot
10
+ - `.env.example`
11
+ - Example local environment variables
12
+ - `scripts/post_slack_render_test.py`
13
+ - Local CLI for posting test messages to Slack
14
+
15
+ ## Local environment
16
+
17
+ Expected local-only environment variables:
18
+
19
+ ```bash
20
+ SLACK_BOT_TOKEN=xoxb-...
21
+ SLACK_TEST_CHANNEL_ID=CXXXXXXXX
22
+ ```
23
+
24
+ `SLACK_BOT_TOKEN` is loaded from `.env` by default when using the script.
25
+
26
+ ## Test channel
27
+
28
+ - Workspace: your Slack workspace
29
+ - Channel: your dedicated render-test channel
30
+ - Channel ID: e.g. `CXXXXXXXX`
31
+
32
+ The bot must already be installed and invited to the channel.
33
+
34
+ ## Commands
35
+
36
+ Post a raw Slack `markdown` block without parser processing:
37
+
38
+ ```bash
39
+ python scripts/post_slack_render_test.py \
40
+ --mode raw \
41
+ --text 'from **bold**.'
42
+ ```
43
+
44
+ Post using `slack_markdown_parser`:
45
+
46
+ ```bash
47
+ python scripts/post_slack_render_test.py \
48
+ --mode parser \
49
+ --text 'from **bold**.'
50
+ ```
51
+
52
+ Preview generated payloads without calling Slack:
53
+
54
+ ```bash
55
+ python scripts/post_slack_render_test.py \
56
+ --mode parser \
57
+ --text 'from **bold**.' \
58
+ --dry-run
59
+ ```
60
+
61
+ Post a markdown file:
62
+
63
+ ```bash
64
+ python scripts/post_slack_render_test.py \
65
+ --mode parser \
66
+ --input-file tests/fixtures/llm_markdown_p0_corpus.md
67
+ ```
68
+
69
+ Generate nested modifier matrix fixtures:
70
+
71
+ ```bash
72
+ python scripts/generate_nested_modifier_matrix.py --content-variant plain
73
+ python scripts/generate_nested_modifier_matrix.py --content-variant parens
74
+ python scripts/generate_nested_modifier_matrix.py --content-variant quotes
75
+ ```
76
+
77
+ Generate focused CJK nested-code fixtures:
78
+
79
+ ```bash
80
+ python scripts/generate_nested_modifier_matrix.py \
81
+ --content-variant plain \
82
+ --locales ja,zh,ko \
83
+ --inners plain,code \
84
+ --output tests/fixtures/slack_cjk_inner_code_matrix.md
85
+ ```
86
+
87
+ ## Output
88
+
89
+ Each posted message prints JSON like:
90
+
91
+ ```json
92
+ {
93
+ "message_index": 1,
94
+ "channel": "CXXXXXXXX",
95
+ "ts": "1773051865.764719",
96
+ "mode": "parser",
97
+ "permalink": "https://your-workspace.slack.com/archives/CXXXXXXXX/p1234567890123456"
98
+ }
99
+ ```
100
+
101
+ Use the `permalink` to open the exact message in Slack and verify rendering in the
102
+ web UI or via Playwright.
103
+
104
+ ## Recommended comparison flow
105
+
106
+ 1. Post the same markdown once with `--mode raw`.
107
+ 2. Post the same markdown once with `--mode parser`.
108
+ 3. Open both permalinks in Slack.
109
+ 4. Compare visible rendering, especially for:
110
+ - bold/italic/strike recognition
111
+ - punctuation boundaries
112
+ - Japanese vs English surrounding text
113
+ - fallback text behavior when relevant
114
+
115
+ For desktop/mobile spot checks, use:
116
+
117
+ - `docs/slack-client-manual-checklist.md`
@@ -12,6 +12,13 @@
12
12
  - Slack Block Kit ブロック(`markdown` / `table`)
13
13
  - 複数テーブル入力時は「1メッセージ1テーブル」を満たすメッセージ群
14
14
 
15
+ ## 設計上の到達点
16
+
17
+ このパーサーは、CommonMark 全体や HTML / リッチドキュメントの完全再現を目標にはしません。
18
+ 目的は、Slack Block Kit の `markdown` / `table` ブロックで送ったときに自然に読めるメッセージを作ることです。
19
+
20
+ Markdown としての厳密さより Slack 上での読みやすさが優先される場合は、Slack 側で安定して読める表現を優先します。
21
+
15
22
  ## 変換パイプライン
16
23
 
17
24
  `convert_markdown_to_slack_blocks` の処理順序:
@@ -28,6 +35,35 @@
28
35
  `convert_markdown_to_slack_messages` は上記の結果を「1メッセージ1テーブル」制約に沿って分割します。
29
36
  `convert_markdown_to_slack_payloads` は、同じ分割結果に `chat.postMessage.text` 用 fallback を付けた payload 群を返します。
30
37
 
38
+ ## 実測ベースの Slack レンダラ挙動
39
+
40
+ 以下は、生成された Block Kit payload を実際の Slack クライアントで確認したときの挙動です。
41
+
42
+ ### Slack が比較的きれいに表示できるもの
43
+
44
+ - asterisk 装飾: `*italic*`, `**bold**`
45
+ - 取消線: `~~strike~~`
46
+ - インラインコードとフェンスドコード
47
+ - bare URL, autolink, Markdown link, 参照リンク, mailto link
48
+ - 箇条書き, 番号付きリスト, タスクリスト, 単純な1段引用
49
+ - Slack `table` ブロック
50
+
51
+ ### Slack 自体の制約として残るもの
52
+
53
+ - `#`, `##`, `###` や setext 見出しは、本当の見出しレベルではなくプレーンテキスト寄りに表示される
54
+ - 多段引用はフル Markdown レンダラほどきれいに出ない
55
+ - 水平線は semantic な区切りではなく、線テキスト寄りの見え方になる
56
+ - Markdown 画像記法は `markdown` ブロック内では埋め込み画像にならない
57
+ - 数式, 生 HTML, HTML comment, `<details>`, admonition 記法, Mermaid は特別なリッチ表示にならない
58
+
59
+ ### このパーサーが補正するもの
60
+
61
+ - `_..._` / `__...__` を Slack 互換の `*...*` / `**...**` に正規化する
62
+ - bare URL は Slack の `markdown` ブロックで安定する autolink 形式(`<https://...>`)へ正規化する
63
+ - 崩れた Markdown テーブルを補完して `table` ブロックへ変換する
64
+ - フェンスドコード内の table 風行をテーブル解析対象から除外する
65
+ - `<foo>` や生 HTML 風タグのような不正な Slack angle token を無害化する
66
+
31
67
  ## Slack 向けサニタイズルール
32
68
 
33
69
  `sanitize_slack_text` の挙動:
@@ -105,7 +141,7 @@ LLM は外枠パイプの省略、セパレータ行の欠落、列数の不一
105
141
 
106
142
  ### 目的
107
143
 
108
- 日本語等の語間スペースがない文で装飾記号が隣接文字と結合し、Slack のレンダリングが崩れる問題を回避します。通常の半角スペースではなくゼロ幅スペースを使うことで、見た目上の不自然な空白を生じさせずに Slack の装飾解析を補助します。
144
+ 日本語等の語間スペースがない文で装飾記号が隣接文字と結合し、Slack のレンダリングが崩れる問題を回避します。基本はゼロ幅スペースで境界を補強し、CJK の密着文脈でインラインコードを内包する装飾だけは、Slack 実表示を優先して可視スペースへフォールバックします。
109
145
 
110
146
  ### 対象パターン
111
147
 
@@ -120,6 +156,10 @@ LLM は外枠パイプの省略、セパレータ行の欠落、列数の不一
120
156
 
121
157
  - **フェンスドコードブロック**(`` ``` ... ``` `` および `~~~ ... ~~~`)内は一切変更しない
122
158
  - インラインコード(`` `...` ``)は除外**しない**(付与対象)
159
+ - `**bold**`、`*italic*`、`~~strike~~` の内側にネストしたインラインコードには個別の ZWSP を付与しない
160
+ - 英語系の境界では外側の装飾トークンはそのまま維持する
161
+ - 日本語・中国語の密着境界では、外側の装飾トークンの前後で不足している側に可視スペースを補う
162
+ - 韓国語の密着境界では、必要に応じて後ろ側の可視スペースを補い、右側に空白があるケースはそのまま維持する
123
163
 
124
164
  ## ZWSP 除去ルール
125
165
 
@@ -144,6 +184,7 @@ LLM は外枠パイプの省略、セパレータ行の欠落、列数の不一
144
184
  `build_fallback_text_from_blocks` は `chat.postMessage.text` に設定する通知プレビュー用テキストを生成:
145
185
 
146
186
  - `markdown` ブロック → ZWSP を除去したテキスト
187
+ - nested inline code の表示安定化のために一時的に入れた可視スペースは、fallback では自然な文に戻す
147
188
  - `table` ブロック → 各行のセルテキストを ` | ` で連結
148
189
  - 各ブロックの出力を空行で区切って結合
149
190
 
@@ -158,3 +199,4 @@ LLM は外枠パイプの省略、セパレータ行の欠落、列数の不一
158
199
  - `mrkdwn` 文字列の生成
159
200
  - 完全な Markdown AST の再現
160
201
  - HTML レンダリング互換
202
+ - Slack `markdown` ブロックが本来対応していない構文に、独自のリッチ表示を与えること
@@ -12,6 +12,13 @@ This document defines the conversion behavior of `slack-markdown-parser`.
12
12
  - Slack Block Kit blocks (`markdown` / `table`)
13
13
  - When the input contains multiple tables, a list of messages that satisfies the "one table per message" rule
14
14
 
15
+ ## Design target
16
+
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 `markdown` and `table` blocks.
19
+
20
+ When Markdown fidelity and Slack readability conflict, readable Slack output takes priority.
21
+
15
22
  ## Conversion pipeline
16
23
 
17
24
  Processing order in `convert_markdown_to_slack_blocks`:
@@ -28,6 +35,35 @@ Processing order in `convert_markdown_to_slack_blocks`:
28
35
  `convert_markdown_to_slack_messages` then splits the resulting block list to satisfy the "one table per message" constraint.
29
36
  `convert_markdown_to_slack_payloads` returns the same split blocks plus fallback `text` values ready for `chat.postMessage`.
30
37
 
38
+ ## Observed Slack renderer behavior
39
+
40
+ The following behaviors are based on practical validation against real Slack clients using the generated Block Kit payloads.
41
+
42
+ ### Behaviors that Slack currently renders well
43
+
44
+ - Asterisk emphasis: `*italic*`, `**bold**`
45
+ - Strikethrough: `~~strike~~`
46
+ - Inline code and fenced code blocks
47
+ - Bare URLs, autolinks, Markdown links, reference-style links, and mailto links
48
+ - Bullet lists, ordered lists, task lists, and simple one-level blockquotes
49
+ - Slack `table` blocks
50
+
51
+ ### Behaviors limited by Slack itself
52
+
53
+ - ATX headings (`#`, `##`, `###`) and setext headings render as plain text rather than true heading levels
54
+ - Nested blockquotes are weaker than in full Markdown renderers
55
+ - Horizontal rules render more like visible line text than semantic separators
56
+ - Markdown image syntax does not become an embedded image inside `markdown` blocks
57
+ - Math, raw HTML, HTML comments, `<details>`, admonition syntax, and Mermaid do not receive special rich rendering
58
+
59
+ ### Behaviors this parser compensates for
60
+
61
+ - `_..._` and `__...__` are normalized into Slack-friendly `*...*` and `**...**`
62
+ - Bare URLs are wrapped into Slack-friendly autolink form (`<https://...>`) before `markdown` block delivery
63
+ - Malformed Markdown tables are repaired before `table` block generation
64
+ - Table-like rows inside fenced code blocks are kept out of table parsing
65
+ - Unsupported Slack angle-bracket tokens such as `<foo>` or raw HTML-like tags are neutralized
66
+
31
67
  ## Slack text sanitize rules
32
68
 
33
69
  Behavior of `sanitize_slack_text`:
@@ -104,7 +140,7 @@ Behavior of `add_zero_width_spaces_to_markdown`:
104
140
 
105
141
  ### Purpose
106
142
 
107
- In languages such as Japanese that do not use spaces between words, formatting markers can attach directly to surrounding characters and break Slack rendering. This library inserts zero-width spaces instead of visible spaces so Slack gets clearer formatting boundaries without changing the visible layout.
143
+ In languages such as Japanese that do not use spaces between words, formatting markers can attach directly to surrounding characters and break Slack rendering. This library primarily inserts zero-width spaces so Slack gets clearer formatting boundaries without changing the visible layout. For some nested inline-code emphasis cases in dense CJK text, it falls back to visible spaces because current Slack rendering is more reliable with explicit spacing.
108
144
 
109
145
  ### Target patterns
110
146
 
@@ -119,6 +155,10 @@ For each formatting token below, if either adjacent side is not a space, tab, ne
119
155
 
120
156
  - Fenced code blocks (both `` ``` ... ``` `` and `~~~ ... ~~~`) are never modified
121
157
  - Inline code (`` `...` ``) is not excluded; it is part of the target set above
158
+ - Inline code nested inside `**bold**`, `*italic*`, or `~~strike~~` is left untouched.
159
+ - For English-like boundaries around those nested combinations, the outer formatting span is preserved as-is.
160
+ - For dense Japanese/Chinese boundaries, visible spaces are inserted on the missing outer side(s) around the outer formatting span.
161
+ - For dense Korean boundaries, a visible trailing space is inserted when needed, while right-space cases are otherwise preserved because Slack already renders them more reliably.
122
162
 
123
163
  ## ZWSP removal rules
124
164
 
@@ -143,6 +183,7 @@ For each formatting token below, if either adjacent side is not a space, tab, ne
143
183
  `build_fallback_text_from_blocks` generates preview text for `chat.postMessage.text` as follows:
144
184
 
145
185
  - `markdown` blocks: text with ZWSP removed
186
+ - Parser-inserted visible spaces used only to stabilize nested inline-code emphasis are normalized back out when building plain fallback text
146
187
  - `table` blocks: join each row's cell text with ` | `
147
188
  - Join block outputs with blank lines between them
148
189
 
@@ -157,3 +198,4 @@ For the same input, the library always returns the same output regardless of env
157
198
  - Generating Slack `mrkdwn` strings
158
199
  - Reconstructing a full Markdown AST
159
200
  - Matching HTML rendering behavior
201
+ - Recreating rich rendering for constructs Slack does not natively support in `markdown` blocks
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "slack-markdown-parser"
7
- version = "2.2.1"
7
+ version = "2.2.2"
8
8
  description = "Convert LLM Markdown into Slack Block Kit markdown/table messages"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  """slack-markdown-parser public package API."""
2
2
 
3
- __version__ = "2.2.1"
3
+ __version__ = "2.2.2"
4
4
  __license__ = "MIT"
5
5
 
6
6
  from .converter import (