slack-markdown-parser 2.2.4__tar.gz → 2.2.5__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.4 → slack_markdown_parser-2.2.5}/CHANGELOG.md +6 -0
  2. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/PKG-INFO +2 -2
  3. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/README-ja.md +1 -1
  4. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/README.md +1 -1
  5. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/docs/spec-ja.md +5 -1
  6. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/docs/spec.md +5 -1
  7. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/pyproject.toml +1 -1
  8. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/slack_markdown_parser/__init__.py +1 -1
  9. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/slack_markdown_parser/converter.py +50 -0
  10. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/slack_markdown_parser.egg-info/PKG-INFO +2 -2
  11. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/tests/test_converter.py +14 -0
  12. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/CONTRIBUTING.md +0 -0
  13. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/LICENSE +0 -0
  14. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/MANIFEST.in +0 -0
  15. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/docs/slack-client-manual-checklist.md +0 -0
  16. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/docs/slack-nested-modifier-findings.md +0 -0
  17. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/docs/slack-render-test-workflow.md +0 -0
  18. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/setup.cfg +0 -0
  19. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/slack_markdown_parser/py.typed +0 -0
  20. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/slack_markdown_parser.egg-info/SOURCES.txt +0 -0
  21. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/slack_markdown_parser.egg-info/dependency_links.txt +0 -0
  22. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/slack_markdown_parser.egg-info/requires.txt +0 -0
  23. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/slack_markdown_parser.egg-info/top_level.txt +0 -0
  24. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/tests/fixtures/llm_markdown_p0_corpus.md +0 -0
  25. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/tests/fixtures/slack_cjk_inner_code_matrix.md +0 -0
  26. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/tests/fixtures/slack_nested_modifier_matrix.md +0 -0
  27. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/tests/fixtures/slack_nested_modifier_matrix_parens.md +0 -0
  28. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/tests/fixtures/slack_nested_modifier_matrix_quotes.md +0 -0
  29. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/tests/test_llm_markdown_p0_corpus.py +0 -0
  30. {slack_markdown_parser-2.2.4 → slack_markdown_parser-2.2.5}/tests/test_nested_modifier_matrix.py +0 -0
@@ -6,6 +6,12 @@ The format is based on Keep a Changelog, and the project follows Semantic Versio
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.2.5] - 2026-03-14
10
+
11
+ ### Fixed
12
+
13
+ - Preserved raw English-like emphasis when it only touches surrounding punctuation, avoiding unnecessary ZWSP around cases such as `**APIYI (apiyi.com)**:` that Slack already renders correctly on its own.
14
+
9
15
  ## [2.2.4] - 2026-03-11
10
16
 
11
17
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slack-markdown-parser
3
- Version: 2.2.4
3
+ Version: 2.2.5
4
4
  Summary: Convert LLM Markdown into Slack Block Kit markdown/table messages
5
5
  Author: darkgaldragon
6
6
  License-Expression: MIT
@@ -61,7 +61,7 @@ If Slack itself does not support a construct in `markdown` blocks, this library
61
61
  - Repair common LLM table issues such as missing outer pipes, missing separator rows, mismatched column counts, and empty cells
62
62
  - Split output into multiple Slack messages when needed to satisfy Slack's "one table per message" constraint
63
63
  - Sanitize ANSI/control characters and neutralize invalid Slack angle-bracket tokens before block generation
64
- - Add ZWSP around inline formatting tokens to reduce rendering issues outside fenced code blocks
64
+ - Add ZWSP around inline formatting tokens to reduce rendering issues outside fenced code blocks, while preserving English-like punctuation-only boundaries that Slack already renders reliably
65
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
66
  - Support Markdown and Slack-style links inside table cells
67
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
@@ -30,7 +30,7 @@ Slack の `markdown` ブロック自体が対応していない構文は、古
30
30
  - LLM が生成する多様な Markdown テーブルで起こり得る記法の揺れ(外枠パイプ不足、セパレータ行不足、列数不一致、空セル)を検知し自動補完。Slack `table` ブロックの `invalid_blocks` エラーを未然に回避
31
31
  - テーブルごとにメッセージを自動分割(Slack の「1メッセージ1テーブル」制約に対応)
32
32
  - ANSI escape / 制御文字を除去し、不正な Slack 角括弧トークンを自動で無害化
33
- - 装飾記号の前後に ZWSP を付与して表示崩れを抑制(フェンスドコードブロック内は除外、インラインコードは付与対象)
33
+ - 装飾記号の前後に ZWSP を付与して表示崩れを抑制(フェンスドコードブロック内は除外、インラインコードは付与対象)。ただし英語系で周囲が句読点だけのケースは、Slack がそのまま安定描画できる場合に ZWSP を増やさない
34
34
  - 日本語・中国語・韓国語の密着文脈で、インラインコードを内包する装飾には可視スペースを補って Slack 表示を安定化
35
35
  - テーブルセル内の Markdown link / Slack link を認識
36
36
  - `chat.postMessage.text` 用の fallback テキストを生成(表示安定化のために入れた ZWSP や人工的な可視スペースは通知文では自然な形に正規化)
@@ -30,7 +30,7 @@ If Slack itself does not support a construct in `markdown` blocks, this library
30
30
  - Repair common LLM table issues such as missing outer pipes, missing separator rows, mismatched column counts, and empty cells
31
31
  - Split output into multiple Slack messages when needed to satisfy Slack's "one table per message" constraint
32
32
  - Sanitize ANSI/control characters and neutralize invalid Slack angle-bracket tokens before block generation
33
- - Add ZWSP around inline formatting tokens to reduce rendering issues outside fenced code blocks
33
+ - Add ZWSP around inline formatting tokens to reduce rendering issues outside fenced code blocks, while preserving English-like punctuation-only boundaries that Slack already renders reliably
34
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
35
  - Support Markdown and Slack-style links inside table cells
36
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
@@ -145,13 +145,17 @@ LLM は外枠パイプの省略、セパレータ行の欠落、列数の不一
145
145
 
146
146
  ### 対象パターン
147
147
 
148
- 以下の装飾記号について、前後のどちらか一方でも隣接文字がスペース・タブ・改行・ZWSP でない場合、または行頭・行末に接している場合、装飾トークン全体を ZWSP(U+200B)で囲って Slack に独立した境界として認識させる:
148
+ 以下の装飾記号について、前後のどちらか一方でも隣接文字がスペース・タブ・改行・ZWSP でない場合、または行頭・行末に接している場合、通常は装飾トークン全体を ZWSP(U+200B)で囲って Slack に独立した境界として認識させる:
149
149
 
150
150
  - `` `code` `` — インラインコード
151
151
  - `**bold**` — 太字
152
152
  - `*italic*` — 斜体
153
153
  - `~~strike~~` — 取消線
154
154
 
155
+ 例外:
156
+
157
+ - 装飾の中身が英語系テキストで、密着している隣接文字が句読点だけの場合は、元のトークンをそのまま維持する。`**APIYI (apiyi.com)**:` のように Slack が素のままで安定描画できるケースで、過剰な ZWSP を入れないため。
158
+
155
159
  ### 除外範囲
156
160
 
157
161
  - **フェンスドコードブロック**(`` ``` ... ``` `` および `~~~ ... ~~~`)内は一切変更しない
@@ -144,13 +144,17 @@ In languages such as Japanese that do not use spaces between words, formatting m
144
144
 
145
145
  ### Target patterns
146
146
 
147
- For each formatting token below, if either adjacent side is not a space, tab, newline, or existing ZWSP, or if the token touches the start or end of a line, the whole token is wrapped in ZWSP (`U+200B`) so Slack recognizes it as a standalone formatting boundary:
147
+ For each formatting token below, if either adjacent side is not a space, tab, newline, or existing ZWSP, or if the token touches the start or end of a line, the whole token is normally wrapped in ZWSP (`U+200B`) so Slack recognizes it as a standalone formatting boundary:
148
148
 
149
149
  - `` `code` ``: inline code
150
150
  - `**bold**`: bold
151
151
  - `*italic*`: italic
152
152
  - `~~strike~~`: strikethrough
153
153
 
154
+ Exception:
155
+
156
+ - If the token body is English-like text and the only tight neighbors are punctuation characters, the raw token is preserved. This avoids over-correcting spans such as `**APIYI (apiyi.com)**:` that Slack already renders correctly without extra ZWSP.
157
+
154
158
  ### Excluded regions
155
159
 
156
160
  - Fenced code blocks (both `` ``` ... ``` `` and `~~~ ... ~~~`) are never modified
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "slack-markdown-parser"
7
- version = "2.2.4"
7
+ version = "2.2.5"
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.4"
3
+ __version__ = "2.2.5"
4
4
  __license__ = "MIT"
5
5
 
6
6
  from .converter import (
@@ -199,6 +199,52 @@ def _find_inline_code_span_end(text: str, start: int) -> int | None:
199
199
  return closing + len(delimiter)
200
200
 
201
201
 
202
+ def _is_punctuation_like(char: str, boundary_chars: set[str]) -> bool:
203
+ return bool(char) and char not in boundary_chars and not char.isalnum()
204
+
205
+
206
+ def _should_preserve_raw_punctuation_emphasis(
207
+ source: str,
208
+ start: int,
209
+ end: int,
210
+ token_text: str,
211
+ boundary_chars: set[str],
212
+ ) -> bool:
213
+ tight_chars = []
214
+ before_char = source[start - 1] if start > 0 else ""
215
+ after_char = source[end] if end < len(source) else ""
216
+
217
+ if before_char and before_char not in boundary_chars:
218
+ tight_chars.append(before_char)
219
+ if after_char and after_char not in boundary_chars:
220
+ tight_chars.append(after_char)
221
+
222
+ if not tight_chars:
223
+ return False
224
+ if any(not _is_punctuation_like(char, boundary_chars) for char in tight_chars):
225
+ return False
226
+ if any(_is_han_or_kana_char(char) or _is_hangul_char(char) for char in token_text):
227
+ return False
228
+
229
+ left_idx = start - 1
230
+ while left_idx >= 0 and source[left_idx] in boundary_chars:
231
+ left_idx -= 1
232
+ if left_idx >= 0 and (
233
+ _is_han_or_kana_char(source[left_idx]) or _is_hangul_char(source[left_idx])
234
+ ):
235
+ return False
236
+
237
+ right_idx = end
238
+ while right_idx < len(source) and source[right_idx] in boundary_chars:
239
+ right_idx += 1
240
+ if right_idx < len(source) and (
241
+ _is_han_or_kana_char(source[right_idx]) or _is_hangul_char(source[right_idx])
242
+ ):
243
+ return False
244
+
245
+ return True
246
+
247
+
202
248
  def normalize_bare_urls_for_slack_markdown(text: str) -> str:
203
249
  """Wrap bare URLs in autolink syntax for stable Slack markdown rendering."""
204
250
  if not text:
@@ -369,6 +415,10 @@ def _format_markdown_with_spacing_metadata(text: str) -> tuple[str, List[int]]:
369
415
  after_safe = end < len(source) and source[end] in boundary_chars
370
416
  if before_safe and after_safe:
371
417
  return match.group(0)
418
+ if _should_preserve_raw_punctuation_emphasis(
419
+ source, start, end, match.group(0), boundary_chars
420
+ ):
421
+ return match.group(0)
372
422
 
373
423
  # When either outer edge is tightly coupled to surrounding text or
374
424
  # punctuation, wrap the whole token so Slack can treat the decoration
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slack-markdown-parser
3
- Version: 2.2.4
3
+ Version: 2.2.5
4
4
  Summary: Convert LLM Markdown into Slack Block Kit markdown/table messages
5
5
  Author: darkgaldragon
6
6
  License-Expression: MIT
@@ -61,7 +61,7 @@ If Slack itself does not support a construct in `markdown` blocks, this library
61
61
  - Repair common LLM table issues such as missing outer pipes, missing separator rows, mismatched column counts, and empty cells
62
62
  - Split output into multiple Slack messages when needed to satisfy Slack's "one table per message" constraint
63
63
  - Sanitize ANSI/control characters and neutralize invalid Slack angle-bracket tokens before block generation
64
- - Add ZWSP around inline formatting tokens to reduce rendering issues outside fenced code blocks
64
+ - Add ZWSP around inline formatting tokens to reduce rendering issues outside fenced code blocks, while preserving English-like punctuation-only boundaries that Slack already renders reliably
65
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
66
  - Support Markdown and Slack-style links inside table cells
67
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
@@ -361,6 +361,20 @@ def test_bold_with_tight_boundary_on_left_is_wrapped_on_both_sides() -> None:
361
361
  )
362
362
 
363
363
 
364
+ def test_english_bold_with_punctuation_on_right_stays_raw() -> None:
365
+ text = "• **APIYI (apiyi.com)**: OpenAI互換"
366
+ converted = add_zero_width_spaces_to_markdown(text)
367
+
368
+ assert converted == text
369
+
370
+
371
+ def test_english_bold_with_japanese_period_stays_raw() -> None:
372
+ text = "• **APIYI (apiyi.com)**。"
373
+ converted = add_zero_width_spaces_to_markdown(text)
374
+
375
+ assert converted == text
376
+
377
+
364
378
  def test_blocks_to_plain_text_and_fallback_generation() -> None:
365
379
  raw = """# Title
366
380