slack-markdown-parser 2.4.0__tar.gz → 2.4.1__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 (18) hide show
  1. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/CHANGELOG.md +7 -0
  2. {slack_markdown_parser-2.4.0/slack_markdown_parser.egg-info → slack_markdown_parser-2.4.1}/PKG-INFO +1 -1
  3. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/docs/spec-ja.md +8 -2
  4. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/docs/spec.md +8 -2
  5. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/pyproject.toml +1 -1
  6. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/slack_markdown_parser/__init__.py +1 -1
  7. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/slack_markdown_parser/converter.py +63 -12
  8. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1/slack_markdown_parser.egg-info}/PKG-INFO +1 -1
  9. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/LICENSE +0 -0
  10. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/MANIFEST.in +0 -0
  11. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/README-ja.md +0 -0
  12. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/README.md +0 -0
  13. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/setup.cfg +0 -0
  14. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/slack_markdown_parser/py.typed +0 -0
  15. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/slack_markdown_parser.egg-info/SOURCES.txt +0 -0
  16. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/slack_markdown_parser.egg-info/dependency_links.txt +0 -0
  17. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/slack_markdown_parser.egg-info/requires.txt +0 -0
  18. {slack_markdown_parser-2.4.0 → slack_markdown_parser-2.4.1}/slack_markdown_parser.egg-info/top_level.txt +0 -0
@@ -6,6 +6,13 @@ The format is based on Keep a Changelog, and the project follows Semantic Versio
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.4.1] - 2026-05-29
10
+
11
+ ### Fixed
12
+
13
+ - Stopped punctuation-terminated emphasis from leaking its literal markers (`**`, `*`, `~~`) in `markdown` blocks. A ZWSP placed just outside a closing marker broke Slack's CommonMark right-flanking check whenever the last inner character was punctuation (e.g. `- **項目:**` at a line end, or `**70.9%→83.0%**、` before CJK punctuation), exposing the raw markers. Chunk boundaries are now treated as safe so no stray ZWSP is appended at line/text ends, and when a marker sits against inner punctuation a ZWSP is inserted just inside it so the run flanks via rule 2a regardless of the following character — including before CJK text and CJK punctuation that Slack does not accept as a flanking neighbor.
14
+ - Stopped preserving English-like punctuation-flanked emphasis raw when its tight neighbor is non-ASCII punctuation (e.g. `**APIYI (apiyi.com)**。` or `Score **70.9%→83.0%**、`). Slack only accepts ASCII punctuation/whitespace as a flanking neighbor, so these now receive the inner ZWSP protection instead of being emitted unchanged.
15
+
9
16
  ## [2.4.0] - 2026-05-14
10
17
 
11
18
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slack-markdown-parser
3
- Version: 2.4.0
3
+ Version: 2.4.1
4
4
  Summary: Convert LLM Markdown into Slack Block Kit messages
5
5
  Author: darkgaldragon
6
6
  License-Expression: MIT
@@ -166,16 +166,22 @@ LLM は外枠パイプの省略、区切り行の欠落、列数の不一致な
166
166
 
167
167
  ### 対象パターン
168
168
 
169
- 以下の装飾記号について、前後のどちらか一方でも隣接文字がスペース・タブ・改行・既存のゼロ幅スペースでない場合、または行頭・行末に接している場合、通常は装飾トークン全体をゼロ幅スペース(`U+200B`)で囲みます。
169
+ 以下の装飾記号について、見た目を変えずに Slack の装飾境界を保つために必要な箇所だけにゼロ幅スペース(`U+200B`)を挿入します。
170
170
 
171
171
  - `` `code` `` — インラインコード
172
172
  - `**bold**` — 太字
173
173
  - `*italic*` — 斜体
174
174
  - `~~strike~~` — 取消線
175
175
 
176
+ ルール:
177
+
178
+ - チャンクの先頭・末尾(行頭・行末・テキスト端、またはフェンスドコードブロックの境界)は安全とみなし、ゼロ幅スペースを付けません。
179
+ - 外側の片方が前後の非境界テキストに密着している場合、その側だけにゼロ幅スペースを付けます。安全(境界)側はそのままにします。
180
+ - 強調マーカー(`**`・`*`・`~~`)の内側が句読点に密着している場合(例 `**注意:**` や `**70.9%→83.0%**`)、マーカーの内側にゼロ幅スペースを挿入します。これによりマーカーの内側隣接文字が非句読点になり、後続が何であっても Slack の CommonMark right-/left-flanking 判定が成立します。Slack が flanking 近傍として認めない CJK テキストや CJK 句読点(`、` / `。`)の直前でも有効です。インラインコードは flanking 規則の対象外なので、このルールから除外します。
181
+
176
182
  例外:
177
183
 
178
- - 装飾の中身が英語系テキストで、密着している隣接文字が句読点だけの場合は、元のトークンをそのまま保つ。`**APIYI (apiyi.com)**:` のように Slack がそのまま表示できるケースで、不要なゼロ幅スペースを増やさないためです。
184
+ - 装飾の中身が英語系テキストで、密着している隣接文字が **ASCII** 句読点だけの場合は、元のトークンをそのまま保ちます。`**APIYI (apiyi.com)**:` のように Slack がそのまま表示できるケースで、不要なゼロ幅スペースを増やさないためです。`、` や `。` のような非ASCII句読点が隣接する場合は保持せず、上記の内側ゼロ幅スペースで保護します。
179
185
 
180
186
  ### 除外範囲
181
187
 
@@ -165,16 +165,22 @@ In languages such as Japanese, Chinese, and Korean that do not usually put space
165
165
 
166
166
  ### Target patterns
167
167
 
168
- For each formatting token below, if either adjacent side is not a space, tab, newline, or existing zero-width space, or if the token touches the start or end of a line, the whole token is normally wrapped in a zero-width space (`U+200B`) so Slack recognizes it as a standalone formatting boundary:
168
+ The library inserts zero-width spaces (`U+200B`) only where they are needed to keep Slack's formatting boundaries intact, without changing the visible layout, for each formatting token below:
169
169
 
170
170
  - `` `code` ``: inline code
171
171
  - `**bold**`: bold
172
172
  - `*italic*`: italic
173
173
  - `~~strike~~`: strikethrough
174
174
 
175
+ Rules:
176
+
177
+ - The start and end of a chunk (a line/text boundary, or the edge of a fenced code block) are treated as safe; no zero-width space is added there.
178
+ - When an outer edge is tight against surrounding non-boundary text, only that edge is padded with a zero-width space. The safe (boundary) edge is left clean.
179
+ - When an emphasis marker (`**`, `*`, `~~`) sits directly against punctuation on its inner side (for example `**注意:**` or `**70.9%→83.0%**`), a zero-width space is inserted just *inside* the marker. This makes the marker's inner neighbor a non-punctuation character, so Slack's CommonMark right-/left-flanking check succeeds regardless of what surrounds the token — including before CJK text and CJK punctuation (`、` / `。`), which Slack does not accept as a flanking neighbor. Inline code spans are exempt from this rule because they do not obey flanking rules.
180
+
175
181
  Exception:
176
182
 
177
- - 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 zero-width spaces.
183
+ - If the token body is English-like text and its only tight neighbors are **ASCII** punctuation characters, the raw token is preserved. This avoids over-correcting spans such as `**APIYI (apiyi.com)**:` that Slack already renders correctly without extra zero-width spaces. A non-ASCII punctuation neighbor such as `、` or `。` is not preserved — it is protected by the inner zero-width space described above.
178
184
 
179
185
  ### Excluded regions
180
186
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "slack-markdown-parser"
7
- version = "2.4.0"
7
+ version = "2.4.1"
8
8
  description = "Convert LLM Markdown into Slack Block Kit 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.4.0"
3
+ __version__ = "2.4.1"
4
4
  __license__ = "MIT"
5
5
 
6
6
  from .converter import (
@@ -515,6 +515,13 @@ def _should_preserve_raw_punctuation_emphasis(
515
515
  return False
516
516
  if any(not _is_punctuation_like(char, boundary_chars) for char in tight_chars):
517
517
  return False
518
+ # Slack only accepts ASCII punctuation (and whitespace) as a flanking
519
+ # neighbor. A non-ASCII punctuation neighbor — e.g. the CJK comma/period
520
+ # ``、``/``。`` — does not satisfy the right-/left-flanking rule, so the
521
+ # token must not be preserved raw; it needs the inner-ZWSP protection in
522
+ # ``wrap_match`` instead.
523
+ if any(ord(char) > 127 for char in tight_chars):
524
+ return False
518
525
  if any(_is_han_or_kana_char(char) or _is_hangul_char(char) for char in token_text):
519
526
  return False
520
527
 
@@ -703,21 +710,65 @@ def _format_markdown_with_spacing_metadata(text: str) -> tuple[str, list[int]]:
703
710
 
704
711
  def wrap_match(match: re.Match[str], source: str) -> str:
705
712
  start, end = match.start(), match.end()
706
- before_safe = start > 0 and source[start - 1] in boundary_chars
707
- after_safe = end < len(source) and source[end] in boundary_chars
713
+ token = match.group(0)
714
+ # The start/end of the chunk are effective boundaries: there is no
715
+ # adjacent text to separate the marker from, so they are safe. Treating
716
+ # them as unsafe used to append a ZWSP right after a closing marker, and
717
+ # when the last content character was punctuation (e.g. ``**注意:**``)
718
+ # the trailing ZWSP made Slack fail the CommonMark right-flanking check
719
+ # and exposed the literal ``**``.
720
+ before_safe = start == 0 or source[start - 1] in boundary_chars
721
+ after_safe = end == len(source) or source[end] in boundary_chars
708
722
  if before_safe and after_safe:
709
- return match.group(0)
723
+ return token
710
724
  if _should_preserve_raw_punctuation_emphasis(
711
- source, start, end, match.group(0), boundary_chars
725
+ source, start, end, token, boundary_chars
712
726
  ):
713
- return match.group(0)
714
-
715
- # When either outer edge is tightly coupled to surrounding text or
716
- # punctuation, wrap the whole token so Slack can treat the decoration
717
- # as a standalone span.
718
- prefix = ZWSP
719
- suffix = ZWSP
720
- return f"{prefix}{match.group(0)}{suffix}"
727
+ return token
728
+
729
+ # When an outer edge is tightly coupled to surrounding text, pad only
730
+ # that edge so Slack can treat the decoration as a standalone span.
731
+ # Padding a safe edge is unnecessary noise.
732
+ prefix = "" if before_safe else ZWSP
733
+ suffix = "" if after_safe else ZWSP
734
+
735
+ # Emphasis markers (``*``/``**``/``~~``) obey CommonMark delimiter-run
736
+ # flanking rules; inline code spans (``` `…` ```) do not. When an
737
+ # emphasis marker sits directly against punctuation on its inner side
738
+ # (``**注意:**``, ``**70%→83%**``) Slack treats the run as a delimiter
739
+ # only when the *outer* neighbour is whitespace or ASCII punctuation; a
740
+ # following CJK character or CJK punctuation (e.g. ``、``) — and even a
741
+ # ZWSP placed just outside the marker — leaves the literal ``**``
742
+ # exposed. Inserting a ZWSP just *inside* the marker makes its inner
743
+ # neighbour a non-punctuation character, so the run flanks via rule 2a
744
+ # regardless of what surrounds the token.
745
+ marker_char = token[0]
746
+ if marker_char != "`":
747
+ marker_len = len(token) - len(token.lstrip(marker_char))
748
+ open_marker = token[:marker_len]
749
+ inner = token[marker_len : len(token) - marker_len]
750
+ close_marker = token[len(token) - marker_len :]
751
+ inner_prefix = (
752
+ ZWSP if inner and _is_punctuation_like(inner[0], boundary_chars) else ""
753
+ )
754
+ inner_suffix = (
755
+ ZWSP
756
+ if inner and _is_punctuation_like(inner[-1], boundary_chars)
757
+ else ""
758
+ )
759
+ if inner_prefix or inner_suffix:
760
+ token = (
761
+ f"{open_marker}{inner_prefix}{inner}{inner_suffix}{close_marker}"
762
+ )
763
+ # The inner ZWSP already lets the marker flank correctly, so an
764
+ # outer ZWSP on the same edge is redundant — and after a closing
765
+ # marker it is precisely what would re-break rendering.
766
+ if inner_prefix:
767
+ prefix = ""
768
+ if inner_suffix:
769
+ suffix = ""
770
+
771
+ return f"{prefix}{token}{suffix}"
721
772
 
722
773
  def wrap_nested_code_emphasis_match(
723
774
  match: re.Match[str],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slack-markdown-parser
3
- Version: 2.4.0
3
+ Version: 2.4.1
4
4
  Summary: Convert LLM Markdown into Slack Block Kit messages
5
5
  Author: darkgaldragon
6
6
  License-Expression: MIT