slack-markdown-parser 2.0.2__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.
- slack_markdown_parser-2.2.2/CHANGELOG.md +69 -0
- slack_markdown_parser-2.2.2/CONTRIBUTING.md +53 -0
- slack_markdown_parser-2.2.2/MANIFEST.in +10 -0
- {slack_markdown_parser-2.0.2/slack_markdown_parser.egg-info → slack_markdown_parser-2.2.2}/PKG-INFO +53 -10
- slack_markdown_parser-2.2.2/README-ja.md +189 -0
- {slack_markdown_parser-2.0.2 → slack_markdown_parser-2.2.2}/README.md +52 -9
- slack_markdown_parser-2.2.2/docs/slack-client-manual-checklist.md +53 -0
- slack_markdown_parser-2.2.2/docs/slack-nested-modifier-findings.md +59 -0
- slack_markdown_parser-2.2.2/docs/slack-render-test-workflow.md +117 -0
- slack_markdown_parser-2.2.2/docs/spec-ja.md +202 -0
- slack_markdown_parser-2.2.2/docs/spec.md +201 -0
- {slack_markdown_parser-2.0.2 → slack_markdown_parser-2.2.2}/pyproject.toml +4 -1
- {slack_markdown_parser-2.0.2 → slack_markdown_parser-2.2.2}/slack_markdown_parser/__init__.py +7 -1
- slack_markdown_parser-2.2.2/slack_markdown_parser/converter.py +1154 -0
- slack_markdown_parser-2.2.2/slack_markdown_parser/py.typed +1 -0
- {slack_markdown_parser-2.0.2 → slack_markdown_parser-2.2.2/slack_markdown_parser.egg-info}/PKG-INFO +53 -10
- slack_markdown_parser-2.2.2/slack_markdown_parser.egg-info/SOURCES.txt +28 -0
- slack_markdown_parser-2.2.2/tests/fixtures/llm_markdown_p0_corpus.md +121 -0
- slack_markdown_parser-2.2.2/tests/fixtures/slack_cjk_inner_code_matrix.md +275 -0
- slack_markdown_parser-2.2.2/tests/fixtures/slack_nested_modifier_matrix.md +365 -0
- slack_markdown_parser-2.2.2/tests/fixtures/slack_nested_modifier_matrix_parens.md +365 -0
- slack_markdown_parser-2.2.2/tests/fixtures/slack_nested_modifier_matrix_quotes.md +365 -0
- slack_markdown_parser-2.2.2/tests/test_converter.py +598 -0
- slack_markdown_parser-2.2.2/tests/test_llm_markdown_p0_corpus.py +49 -0
- slack_markdown_parser-2.2.2/tests/test_nested_modifier_matrix.py +86 -0
- slack_markdown_parser-2.0.2/slack_markdown_parser/converter.py +0 -572
- slack_markdown_parser-2.0.2/slack_markdown_parser.egg-info/SOURCES.txt +0 -11
- slack_markdown_parser-2.0.2/tests/test_converter.py +0 -195
- {slack_markdown_parser-2.0.2 → slack_markdown_parser-2.2.2}/LICENSE +0 -0
- {slack_markdown_parser-2.0.2 → slack_markdown_parser-2.2.2}/setup.cfg +0 -0
- {slack_markdown_parser-2.0.2 → slack_markdown_parser-2.2.2}/slack_markdown_parser.egg-info/dependency_links.txt +0 -0
- {slack_markdown_parser-2.0.2 → slack_markdown_parser-2.2.2}/slack_markdown_parser.egg-info/requires.txt +0 -0
- {slack_markdown_parser-2.0.2 → slack_markdown_parser-2.2.2}/slack_markdown_parser.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on Keep a Changelog, and the project follows Semantic Versioning.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [2.2.2] - 2026-03-10
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added Slack render-test tooling, generated regression fixtures, and maintainer docs for validating real Slack client rendering across Web, desktop, and mobile.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
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.
|
|
25
|
+
|
|
26
|
+
## [2.2.1] - 2026-03-07
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Normalized underscore emphasis (`_..._`, `__...__`) into Slack-compatible asterisk emphasis before table parsing.
|
|
31
|
+
- Excluded fenced code blocks from table normalization and segment splitting so table-like rows inside code fences stay in `markdown` blocks.
|
|
32
|
+
- Extended fenced-code preservation to tilde fences (`~~~ ... ~~~`) when inserting ZWSP.
|
|
33
|
+
- Improved heading-plus-table rescue so inputs like `### Heading ... Header A | Header B` preserve multi-word first header cells more naturally.
|
|
34
|
+
|
|
35
|
+
## [2.0.2] - 2026-03-06
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- Improved public API wording and refreshed the English and Japanese README files.
|
|
40
|
+
- Clarified project scope for `mrkdwn`-only clients and fallback text behavior.
|
|
41
|
+
- Refined validation samples and screenshots in the documentation.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- Improved ZWSP padding around punctuation-adjacent Markdown.
|
|
46
|
+
- Expanded parser edge-case handling and related documentation.
|
|
47
|
+
|
|
48
|
+
## [2.0.1] - 2026-03-05
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- Added initial GitHub Actions coverage for tests and package builds.
|
|
53
|
+
- Added OSS-facing documentation and a real rendering example in the README.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- Trimmed the repository to the core distributable package and refreshed maintainer contact details.
|
|
58
|
+
- Updated GitHub Actions dependencies to newer major versions.
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
|
|
62
|
+
- Added ZWSP padding for inline code markers when adjacent to surrounding text.
|
|
63
|
+
|
|
64
|
+
## [2.0.0] - 2026-03-05
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- Published the packaged OSS release of `slack-markdown-parser`.
|
|
69
|
+
- Added packaging metadata, documentation, and review/reference materials for the open-source release.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in improving `slack-markdown-parser`.
|
|
4
|
+
Small fixes, bug reports, documentation updates, and tests are all welcome.
|
|
5
|
+
English and Japanese contributions are both welcome.
|
|
6
|
+
|
|
7
|
+
## Development setup
|
|
8
|
+
|
|
9
|
+
This project supports Python 3.10 through 3.13.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
python -m venv .venv
|
|
13
|
+
source .venv/bin/activate
|
|
14
|
+
python -m pip install --upgrade pip
|
|
15
|
+
pip install -e ".[dev]"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Local checks
|
|
19
|
+
|
|
20
|
+
Run these commands before opening a pull request:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
python -m pytest -q
|
|
24
|
+
python -m ruff check .
|
|
25
|
+
python -m black --check .
|
|
26
|
+
python -m build
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If you are changing Markdown parsing behavior, please add or update automated tests in `tests/`.
|
|
30
|
+
|
|
31
|
+
## Pull requests
|
|
32
|
+
|
|
33
|
+
- Keep changes focused and explain the user-facing impact.
|
|
34
|
+
- Add or update tests when parser behavior changes.
|
|
35
|
+
- Update `README.md`, `README-ja.md`, or `docs/spec*.md` when public behavior or examples change.
|
|
36
|
+
- Conventional Commits are encouraged for commit messages, but not required for contributors.
|
|
37
|
+
- Make sure CI passes before requesting review.
|
|
38
|
+
|
|
39
|
+
## Reporting issues
|
|
40
|
+
|
|
41
|
+
Use the GitHub issue templates when possible.
|
|
42
|
+
Helpful reports usually include:
|
|
43
|
+
|
|
44
|
+
- A short description of the problem
|
|
45
|
+
- A minimal Markdown sample that reproduces it
|
|
46
|
+
- Expected output and actual output
|
|
47
|
+
- Python version and installation method
|
|
48
|
+
- Slack rendering details if the issue is UI-specific
|
|
49
|
+
|
|
50
|
+
## Release notes
|
|
51
|
+
|
|
52
|
+
Project maintainers keep release history in `CHANGELOG.md`.
|
|
53
|
+
If your pull request affects users, please include a short changelog note in the PR description.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
include LICENSE
|
|
2
|
+
include README.md
|
|
3
|
+
include README-ja.md
|
|
4
|
+
include CONTRIBUTING.md
|
|
5
|
+
include CHANGELOG.md
|
|
6
|
+
recursive-include docs *.md
|
|
7
|
+
prune docs/_internal
|
|
8
|
+
recursive-include tests *.py *.md
|
|
9
|
+
recursive-include slack_markdown_parser py.typed
|
|
10
|
+
global-exclude __pycache__ *.py[cod]
|
{slack_markdown_parser-2.0.2/slack_markdown_parser.egg-info → slack_markdown_parser-2.2.2}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: slack-markdown-parser
|
|
3
|
-
Version: 2.
|
|
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,17 +48,51 @@ 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 |
|
|
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
|
|
57
60
|
- Convert Markdown tables into Slack `table` blocks
|
|
58
61
|
- Repair common LLM table issues such as missing outer pipes, missing separator rows, mismatched column counts, and empty cells
|
|
59
62
|
- Split output into multiple Slack messages when needed to satisfy Slack's "one table per message" constraint
|
|
63
|
+
- Sanitize ANSI/control characters and neutralize invalid Slack angle-bracket tokens before block generation
|
|
60
64
|
- Add ZWSP around inline formatting tokens to reduce rendering issues outside fenced code blocks
|
|
61
|
-
-
|
|
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
|
|
66
|
+
- Support Markdown and Slack-style links inside table cells
|
|
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
|
|
68
|
+
- Accept raw LLM Markdown without tightly constraining the model prompt, using best-effort sanitize and table repair before Slack delivery
|
|
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
|
|
62
96
|
|
|
63
97
|
## Requirements
|
|
64
98
|
|
|
@@ -75,8 +109,7 @@ pip install slack-markdown-parser
|
|
|
75
109
|
|
|
76
110
|
```python
|
|
77
111
|
from slack_markdown_parser import (
|
|
78
|
-
|
|
79
|
-
convert_markdown_to_slack_messages,
|
|
112
|
+
convert_markdown_to_slack_payloads,
|
|
80
113
|
)
|
|
81
114
|
|
|
82
115
|
markdown = """
|
|
@@ -88,11 +121,7 @@ markdown = """
|
|
|
88
121
|
| UI | *In progress* |
|
|
89
122
|
"""
|
|
90
123
|
|
|
91
|
-
for
|
|
92
|
-
payload = {
|
|
93
|
-
"blocks": blocks,
|
|
94
|
-
"text": build_fallback_text_from_blocks(blocks) or "report",
|
|
95
|
-
}
|
|
124
|
+
for payload in convert_markdown_to_slack_payloads(markdown):
|
|
96
125
|
print(payload)
|
|
97
126
|
```
|
|
98
127
|
|
|
@@ -146,6 +175,7 @@ Example Slack bot rendering (`markdown` + `table` blocks):
|
|
|
146
175
|
| Function | Description |
|
|
147
176
|
|---|---|
|
|
148
177
|
| `convert_markdown_to_slack_messages(markdown_text) -> list[list[dict]]` | Convert Markdown into Slack messages already split around table blocks. |
|
|
178
|
+
| `convert_markdown_to_slack_payloads(markdown_text) -> list[dict]` | Convert Markdown into Slack-ready payloads with both `blocks` and fallback `text`. |
|
|
149
179
|
| `convert_markdown_to_slack_blocks(markdown_text) -> list[dict]` | Convert Markdown into a flat Block Kit block list. |
|
|
150
180
|
| `build_fallback_text_from_blocks(blocks) -> str` | Build fallback text suitable for `chat.postMessage.text`. |
|
|
151
181
|
| `blocks_to_plain_text(blocks) -> str` | Convert blocks into plain text. |
|
|
@@ -157,6 +187,7 @@ Example Slack bot rendering (`markdown` + `table` blocks):
|
|
|
157
187
|
| `normalize_markdown_tables(markdown_text) -> str` | Normalize Markdown table syntax before conversion. |
|
|
158
188
|
| `add_zero_width_spaces_to_markdown(text) -> str` | Insert ZWSP around formatting tokens where Slack needs stronger boundaries. |
|
|
159
189
|
| `decode_html_entities(text) -> str` | Decode HTML entities before parsing. |
|
|
190
|
+
| `sanitize_slack_text(text) -> str` | Remove ANSI/control noise and neutralize invalid Slack angle-bracket tokens. |
|
|
160
191
|
| `strip_zero_width_spaces(text) -> str` | Remove ZWSP (`U+200B`) and BOM (`U+FEFF`) while preserving join-control characters such as ZWJ. |
|
|
161
192
|
|
|
162
193
|
### Lower-level exported helpers
|
|
@@ -175,10 +206,22 @@ 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`
|
|
181
215
|
|
|
216
|
+
## Contributing
|
|
217
|
+
|
|
218
|
+
Contributions, bug reports, and documentation improvements are welcome.
|
|
219
|
+
Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening an issue or pull request.
|
|
220
|
+
|
|
221
|
+
## Changelog
|
|
222
|
+
|
|
223
|
+
Version history is maintained in [CHANGELOG.md](CHANGELOG.md).
|
|
224
|
+
|
|
182
225
|
## Contact
|
|
183
226
|
|
|
184
227
|
- GitHub Issues / Pull Requests
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# slack-markdown-parser
|
|
2
|
+
|
|
3
|
+
LLM が生成する標準的な Markdown を Slack Block Kit(`markdown` + `table` ブロック)に変換する Python ライブラリです。
|
|
4
|
+
|
|
5
|
+
## 背景
|
|
6
|
+
|
|
7
|
+
Slack で AI BOT を運用する場合、従来は Slack 独自の `mrkdwn` 形式に変換していましたが、以下の課題がありました。
|
|
8
|
+
|
|
9
|
+
- **変換コスト**: LLM は標準 Markdown を出力するため、`mrkdwn` に合わせる変換ロジックやプロンプト制御が必要
|
|
10
|
+
- **装飾崩れ**: 日本語のように語間スペースがない文では、`mrkdwn` の装飾記号(`*`, `~` など)が正しくレンダリングされず記号がそのまま露出することがあり、ロジックやプロンプトでの制御も難しい
|
|
11
|
+
- **テーブル非対応**: `mrkdwn` にはテーブル構文がなく、リストへ変換するなどの代替処理が必要
|
|
12
|
+
|
|
13
|
+
## 設計方針
|
|
14
|
+
|
|
15
|
+
Slack Block Kit の `markdown` ブロック(標準 Markdown 構文をそのまま受け付ける)と `table` ブロックを活用し、上記の課題を解消します。
|
|
16
|
+
|
|
17
|
+
| 課題 | 解決手段 |
|
|
18
|
+
|---|---|
|
|
19
|
+
| 変換コスト | `markdown` ブロックが標準 Markdown をそのまま受け付けるため、LLM 出力を変換せず利用可能 |
|
|
20
|
+
| 装飾崩れ | 基本は ZWSP(ゼロ幅スペース U+200B)で装飾境界を補強し、CJK の密着文脈でインラインコードを内包する装飾が崩れる場合のみ、言語別ルールで可視スペースも使ってレンダリングを安定化する |
|
|
21
|
+
| テーブル非対応 | Markdown テーブルを検知して `table` ブロックに変換。LLM の出力する多様なテーブル記法の揺れも自動補完し `invalid_blocks` エラーを回避 |
|
|
22
|
+
|
|
23
|
+
このライブラリの目標は、CommonMark や HTML を完全再現することではなく、Slack 上で自然に読める表示を作ることです。
|
|
24
|
+
Slack の `markdown` ブロック自体が対応していない構文は、古い `mrkdwn` へ無理に書き換えるより、安全なプレーンテキスト表示や `table` ブロック化を優先します。
|
|
25
|
+
|
|
26
|
+
## 主な機能
|
|
27
|
+
|
|
28
|
+
- 標準 Markdown テキストを `markdown` ブロックに変換
|
|
29
|
+
- Markdown テーブルを `table` ブロックに変換(セル内の太字・斜体・取消線・インラインコードを認識)
|
|
30
|
+
- LLM が生成する多様な Markdown テーブルで起こり得る記法の揺れ(外枠パイプ不足、セパレータ行不足、列数不一致、空セル)を検知し自動補完。Slack `table` ブロックの `invalid_blocks` エラーを未然に回避
|
|
31
|
+
- テーブルごとにメッセージを自動分割(Slack の「1メッセージ1テーブル」制約に対応)
|
|
32
|
+
- ANSI escape / 制御文字を除去し、不正な Slack 角括弧トークンを自動で無害化
|
|
33
|
+
- 装飾記号の前後に ZWSP を付与して表示崩れを抑制(フェンスドコードブロック内は除外、インラインコードは付与対象)
|
|
34
|
+
- 日本語・中国語・韓国語の密着文脈で、インラインコードを内包する装飾には可視スペースを補って Slack 表示を安定化
|
|
35
|
+
- テーブルセル内の Markdown link / Slack link を認識
|
|
36
|
+
- `chat.postMessage.text` 用の fallback テキストを生成(表示安定化のために入れた ZWSP や人工的な可視スペースは通知文では自然な形に正規化)
|
|
37
|
+
- モデル側で Markdown を厳密に制御しなくてもよいよう、Slack 送信前にベストエフォートでサニタイズとテーブル補完を行う
|
|
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
|
+
|
|
66
|
+
## 利用前提
|
|
67
|
+
|
|
68
|
+
- Slack Block Kit の `blocks` で `markdown` / `table` ブロックを送信できる実装が必要です。
|
|
69
|
+
- `text` / `mrkdwn` のみ送信可能な経路(例: 一部の Slack MCP ツール)では利用できません。
|
|
70
|
+
|
|
71
|
+
## インストール
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install slack-markdown-parser
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 最小利用例
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from slack_markdown_parser import (
|
|
81
|
+
convert_markdown_to_slack_payloads,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
markdown = """
|
|
85
|
+
# Weekly Report
|
|
86
|
+
|
|
87
|
+
| Team | Status |
|
|
88
|
+
|---|---|
|
|
89
|
+
| API | **On track** |
|
|
90
|
+
| UI | *In progress* |
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
for payload in convert_markdown_to_slack_payloads(markdown):
|
|
94
|
+
print(payload)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`convert_markdown_to_slack_messages` は、複数テーブルを含む入力を Slack 制約に合わせて複数メッセージへ分割します。
|
|
98
|
+
|
|
99
|
+
## 入出力イメージ
|
|
100
|
+
|
|
101
|
+
検証テキスト:
|
|
102
|
+
|
|
103
|
+
````markdown
|
|
104
|
+
# 週次プロダクト更新
|
|
105
|
+
|
|
106
|
+
今週は**検索速度改善**と*UI調整*を進めました。旧仕様は~~廃止予定~~です。
|
|
107
|
+
詳細ログIDは`run-20260305-02`です。
|
|
108
|
+
参考: https://example.com/changelog
|
|
109
|
+
|
|
110
|
+
- APIの**レスポンス改善**
|
|
111
|
+
- *キャッシュヒット率*を改善
|
|
112
|
+
- タイムアウト設定を調整
|
|
113
|
+
- バッチ処理の安定化
|
|
114
|
+
- リトライ回数を統一
|
|
115
|
+
- ドキュメント更新
|
|
116
|
+
|
|
117
|
+
カテゴリ | 状況 | 担当
|
|
118
|
+
API | **進行中** | Team A
|
|
119
|
+
UI | *確認中* | Team B
|
|
120
|
+
QA | ~~保留~~ | Team C
|
|
121
|
+
|
|
122
|
+
> 注意: 本番反映は 3/8 10:00 JST を予定
|
|
123
|
+
|
|
124
|
+
1. リリースノート最終確認
|
|
125
|
+
1. 変更点の表記統一
|
|
126
|
+
2. 影響範囲の追記
|
|
127
|
+
2. 監視アラートしきい値調整
|
|
128
|
+
1. `warning`閾値の更新
|
|
129
|
+
3. QA再確認
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
./deploy.sh production
|
|
133
|
+
```
|
|
134
|
+
````
|
|
135
|
+
|
|
136
|
+
実際のSlack BOTでの表示例(`markdown` + `table` ブロック):
|
|
137
|
+
|
|
138
|
+

|
|
139
|
+
|
|
140
|
+
## ライブラリの公開インターフェース
|
|
141
|
+
|
|
142
|
+
### メイン関数(公開関数)
|
|
143
|
+
|
|
144
|
+
| 関数 | 説明 |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `convert_markdown_to_slack_messages(markdown_text) → list[list[dict]]` | Markdown をテーブル分割済みのメッセージ群に変換(主要エントリポイント) |
|
|
147
|
+
| `convert_markdown_to_slack_payloads(markdown_text) → list[dict]` | `blocks` と fallback `text` を含む Slack 送信用 payload 群へ変換 |
|
|
148
|
+
| `convert_markdown_to_slack_blocks(markdown_text) → list[dict]` | Markdown を Block Kit ブロックのリストに変換 |
|
|
149
|
+
| `build_fallback_text_from_blocks(blocks) → str` | ブロックから `chat.postMessage.text` 用 fallback テキストを生成 |
|
|
150
|
+
| `blocks_to_plain_text(blocks) → str` | ブロックからプレーンテキストを生成 |
|
|
151
|
+
|
|
152
|
+
### ユーティリティ関数(公開関数)
|
|
153
|
+
|
|
154
|
+
| 関数 | 説明 |
|
|
155
|
+
|---|---|
|
|
156
|
+
| `normalize_markdown_tables(markdown_text) → str` | テーブル記法を正規化(パイプ補完、セパレータ生成、列数調整) |
|
|
157
|
+
| `add_zero_width_spaces_to_markdown(text) → str` | 装飾記号の前後に ZWSP を挿入(フェンスドコードブロック内は除外) |
|
|
158
|
+
| `decode_html_entities(text) → str` | HTML エンティティをデコード |
|
|
159
|
+
| `sanitize_slack_text(text) → str` | ANSI / 制御文字を除去し、不正な Slack 角括弧トークンを無害化 |
|
|
160
|
+
| `strip_zero_width_spaces(text) → str` | ZWSP (U+200B) と BOM (U+FEFF) を除去(ZWJ 等の結合制御文字は保持) |
|
|
161
|
+
|
|
162
|
+
## 仕様
|
|
163
|
+
|
|
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)
|
|
169
|
+
- 非対応:
|
|
170
|
+
- `mrkdwn` 文字列の生成
|
|
171
|
+
- `mrkdwn` のみ送信可能なクライアント/MCP ツール
|
|
172
|
+
|
|
173
|
+
## コントリビュート
|
|
174
|
+
|
|
175
|
+
不具合報告、ドキュメント改善、コードの提案を歓迎します。
|
|
176
|
+
Issue / Pull Request を作成する前に [CONTRIBUTING.md](CONTRIBUTING.md) を参照してください。
|
|
177
|
+
|
|
178
|
+
## 変更履歴
|
|
179
|
+
|
|
180
|
+
リリース履歴は [CHANGELOG.md](CHANGELOG.md) で管理しています。
|
|
181
|
+
|
|
182
|
+
## 連絡先
|
|
183
|
+
|
|
184
|
+
- GitHub Issue / Pull Request
|
|
185
|
+
- X: [@darkgaldragon](https://x.com/darkgaldragon)
|
|
186
|
+
|
|
187
|
+
## ライセンス
|
|
188
|
+
|
|
189
|
+
MIT
|
|
@@ -17,17 +17,51 @@ 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 |
|
|
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
|
|
26
29
|
- Convert Markdown tables into Slack `table` blocks
|
|
27
30
|
- Repair common LLM table issues such as missing outer pipes, missing separator rows, mismatched column counts, and empty cells
|
|
28
31
|
- Split output into multiple Slack messages when needed to satisfy Slack's "one table per message" constraint
|
|
32
|
+
- Sanitize ANSI/control characters and neutralize invalid Slack angle-bracket tokens before block generation
|
|
29
33
|
- Add ZWSP around inline formatting tokens to reduce rendering issues outside fenced code blocks
|
|
30
|
-
-
|
|
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
|
|
35
|
+
- Support Markdown and Slack-style links inside table cells
|
|
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
|
|
37
|
+
- Accept raw LLM Markdown without tightly constraining the model prompt, using best-effort sanitize and table repair before Slack delivery
|
|
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
|
|
31
65
|
|
|
32
66
|
## Requirements
|
|
33
67
|
|
|
@@ -44,8 +78,7 @@ pip install slack-markdown-parser
|
|
|
44
78
|
|
|
45
79
|
```python
|
|
46
80
|
from slack_markdown_parser import (
|
|
47
|
-
|
|
48
|
-
convert_markdown_to_slack_messages,
|
|
81
|
+
convert_markdown_to_slack_payloads,
|
|
49
82
|
)
|
|
50
83
|
|
|
51
84
|
markdown = """
|
|
@@ -57,11 +90,7 @@ markdown = """
|
|
|
57
90
|
| UI | *In progress* |
|
|
58
91
|
"""
|
|
59
92
|
|
|
60
|
-
for
|
|
61
|
-
payload = {
|
|
62
|
-
"blocks": blocks,
|
|
63
|
-
"text": build_fallback_text_from_blocks(blocks) or "report",
|
|
64
|
-
}
|
|
93
|
+
for payload in convert_markdown_to_slack_payloads(markdown):
|
|
65
94
|
print(payload)
|
|
66
95
|
```
|
|
67
96
|
|
|
@@ -115,6 +144,7 @@ Example Slack bot rendering (`markdown` + `table` blocks):
|
|
|
115
144
|
| Function | Description |
|
|
116
145
|
|---|---|
|
|
117
146
|
| `convert_markdown_to_slack_messages(markdown_text) -> list[list[dict]]` | Convert Markdown into Slack messages already split around table blocks. |
|
|
147
|
+
| `convert_markdown_to_slack_payloads(markdown_text) -> list[dict]` | Convert Markdown into Slack-ready payloads with both `blocks` and fallback `text`. |
|
|
118
148
|
| `convert_markdown_to_slack_blocks(markdown_text) -> list[dict]` | Convert Markdown into a flat Block Kit block list. |
|
|
119
149
|
| `build_fallback_text_from_blocks(blocks) -> str` | Build fallback text suitable for `chat.postMessage.text`. |
|
|
120
150
|
| `blocks_to_plain_text(blocks) -> str` | Convert blocks into plain text. |
|
|
@@ -126,6 +156,7 @@ Example Slack bot rendering (`markdown` + `table` blocks):
|
|
|
126
156
|
| `normalize_markdown_tables(markdown_text) -> str` | Normalize Markdown table syntax before conversion. |
|
|
127
157
|
| `add_zero_width_spaces_to_markdown(text) -> str` | Insert ZWSP around formatting tokens where Slack needs stronger boundaries. |
|
|
128
158
|
| `decode_html_entities(text) -> str` | Decode HTML entities before parsing. |
|
|
159
|
+
| `sanitize_slack_text(text) -> str` | Remove ANSI/control noise and neutralize invalid Slack angle-bracket tokens. |
|
|
129
160
|
| `strip_zero_width_spaces(text) -> str` | Remove ZWSP (`U+200B`) and BOM (`U+FEFF`) while preserving join-control characters such as ZWJ. |
|
|
130
161
|
|
|
131
162
|
### Lower-level exported helpers
|
|
@@ -144,10 +175,22 @@ 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`
|
|
150
184
|
|
|
185
|
+
## Contributing
|
|
186
|
+
|
|
187
|
+
Contributions, bug reports, and documentation improvements are welcome.
|
|
188
|
+
Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening an issue or pull request.
|
|
189
|
+
|
|
190
|
+
## Changelog
|
|
191
|
+
|
|
192
|
+
Version history is maintained in [CHANGELOG.md](CHANGELOG.md).
|
|
193
|
+
|
|
151
194
|
## Contact
|
|
152
195
|
|
|
153
196
|
- GitHub Issues / Pull Requests
|
|
@@ -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
|