Skip to content

fix(workbench): close bold that ends in punctuation before Chinese text in Bot replies - #6658

Merged
M4n5ter merged 8 commits into
rust-previewfrom
fix/bot-markdown-cjk-emphasis
Nov 6, 2026
Merged

M4n5ter merged 8 commits into
rust-previewfrom
fix/bot-markdown-cjk-emphasis

Conversation

@liugddx

@liugddx liugddx commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

Summary

A Bot reply that ends bold text with punctuation and continues straight into Chinese showed its asterisks, for example **回升能否守住:**如果很快跌回…. CommonMark closes ** only if the character before it is not punctuation, or the one after it is whitespace or punctuation; Chinese is written without spaces, so these pairs never close. I checked this with the markdown-rs 1.0.0 that the UI uses: **回升能否守住:**如果… and **Note:**Then text produce no strong node, while **回升能否守住**:如果… and **Note:** Then text do.

Where a **…** pair fails for that reason, the punctuation at the edge of the bold text moves outside the markers (**回升能否守住**:如果…). A balanced wrapper around the bold text moves out as a pair (他说**“不行”**然后 → 他说“**不行**”然后), so nothing is half bold. The visible text is the same and the pair closes. The change is a small pure function, closeCjkEmphasis, applied to the Bot's SendUser replies.

It is a text pass, not a parser, so it only rewrites what it can recognise as plain prose: it first finds what must not be touched (escapes, code spans of matching length including ones over several lines, link and image destinations, reference definitions, autolinks, HTML tags, raw URLs, fenced and indented code) and rewrites only the prose around them, line by line in a list, quote or table. Pairs of ** follow CommonMark's opener/closer rule, so a literal 2 ** 3 is not paired with a real opener. A fence follows CommonMark (a closing line may carry only blanks after its run, a backtick fence's info string cannot contain a backtick, a shorter run does not close a longer one). It leaves a pair as written when it cannot fix it without splitting bold text: a bracket that closes inside the bold text (**营收(同比 +12%)**增长) or a unit such as % is never separated.

Not in this PR: ordinary assistant messages share the parser and the problem. MarkdownBody also carries source offsets for selection and the streaming prefix check, so rewriting its text needs its own look. A fix inside the parser would cover everything, but third-party/nsh is synchronized from upstream, so I did not change it.

Verification

  • Ten tests for closeCjkEmphasis cover the target pattern, text that must stay byte-identical, code/links/URLs/HTML/escapes beside fixed prose, nested and look-alike fences, list items with code or links, nested list items, quotes, task lists, </> in prose, balanced wrappers, brackets and units that must not be split, 2 ** 3, and a second pass changing nothing.
  • Against the real parser: 34 inputs, the review's counterexamples among them, were run through closeCjkEmphasis and parsed with markdown-rs 1.0.0 (GFM) before and after. In every case the code, inline code, HTML, link and image targets and definitions are identical, no bold is lost, and the visible text (ignoring the markers) is the same; every rewritten target pattern now yields a strong node. This was a throwaway program, not committed.
  • Biome, the ui_types typecheck and the workbench tests (115) pass.
  • Not run: the desktop app after this change.

AI use

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Code (Claude Opus 5.5) wrote the code, the tests and this description; the contributor reviewed it. The commit carries Generated-by: Claude Code.

Checklist

  • Tests cover the change and fail without it
  • Format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

…xt in Bot replies

CommonMark closes `**` only when the character before it is not punctuation or the one
after it is whitespace or punctuation. Chinese has no spaces, so a Bot reply such as
`**回升能否守住:**如果…` never closed and its asterisks were shown. Where a pair would
fail for that reason, the punctuation at the edge of the bold text moves outside the
markers; the visible text is the same and the pair now closes. Code (fenced or inline)
and text that already works are left as written.

Applied to Bot replies only. Other messages share the parser and the problem; they also
carry source offsets, so they are left for a separate change.

Generated-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed all three changed files at this commit, including the BotReply call site and the complete normalizer and tests. The six committed tests pass in my Node 22 run. I also ran the extracted production function against markdown-rs 1.0.0 with GFM options, which the NSH Markdown parser uses.

P2 — preserve Markdown code and link destinations before rewriting emphasis (products/maka/workbench/app/markdownEmphasis.ts:63–77). The intended Chinese punctuation example now produces a strong node, but the same preprocessing also changes valid content outside prose. For example, [查看](https://example.test/**path:**next) changes its actual href to https://example.test/**path**:next. A multiline inline-code span, a double-backtick span containing a single backtick, and an indented code block also have their literal code changed. These are before/after parser results, rather than guesses about delimiter handling. The inline comment gives concrete inputs. Restrict rewriting to appropriate Markdown text nodes or otherwise preserve those contexts.

The changes merge cleanly with rust-preview@72162299. CI run 42152430622 failed: native7 repeats the already identified instruction-ack fixture failure from #6655; CLI6 has lifecycle/rejected-send waits, and Desktop2 cannot find “Choose another folder…”. I read those failure logs but have not established that this PR caused the waits. They are not additional findings here. No protocol or database migration changes. I did not run the native desktop or the complete Rust suite.


Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

// Inline code spans stay as written; the text between them is fixed.
return line
.split(/(`+[^`]*`+)/u)
.map((part, index) => (index % 2 === 1 ? part : fixPairs(part)))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Keep code and link destinations outside the emphasis rewrite. This runs fixPairs on everything not recognized by a single-line backtick split. With the actual function and markdown-rs 1.0.0/GFM, [查看](https://example.test/**path:**next) changes the link destination to https://example.test/**path**:next. Valid code is also rewritten, for example:

`line one
**原文:**继续
line three`

``a`**原文:**继续``

Four-space-indented code is changed too. These inputs were preserved when BotReply passed the original Markdown directly. Please preserve parser-recognized code/link contexts, including multiline and matching-length code delimiters, before moving punctuation.


Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

@github-actions github-actions Bot added the effort/M Under 500 readable lines label Nov 6, 2026
liugddx and others added 2 commits November 6, 2026 14:51
The first version split a line on single-line backtick spans and rewrote the rest, so
it could change a link destination (`[查看](https://x/**path:**next)`), a code span that
runs over several lines, a span closed by a longer run of backticks, or indented code.

A paragraph is now rewritten only when it has no code span, link, image, URL, HTML tag,
reference or escape, and no indented line. Anything else reaches the parser exactly as
written. Fenced code is still skipped line by line.

Generated-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@Astro-Han Astro-Han left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rechecked the full three-file change at d34a576 against the earlier 8c58f45 review. The previous link-destination, multiline inline-code, double-backtick, indented-code and escaped-text examples are now preserved. All eight committed tests pass in my Node 22 run; the intended Chinese emphasis still becomes a strong node with the actual markdown-rs 1.0.0 GFM parser.

P2 — the code-preservation issue remains for a fence-like line with trailing text. At markdownEmphasis.ts:80, a matching backtick run clears fenced even when non-whitespace follows it. For example, in a fenced Markdown example containing a line with three backticks followed by not a closing fence, GFM keeps the following **原文:**继续 inside the code block. This function changes it to **原文**:继续. I ran both strings through the GFM parser and confirmed the code node itself changes. The inline comment includes the complete input. Validate the complete closing-fence syntax, or preserve code blocks through the existing parser, before applying prose rewrites. The old concrete examples are fixed; the broader code-preservation finding is not fully resolved.

The change merges cleanly with current rust-preview@41767ca7. CI run 42177126489 failed only the native7 instruction-ack fixture plus its aggregate gate: the original log checks out merge 0523722 into 7216229 and fails with opened versus before_instructions_ack (40/41 passed). This is the previously established #6655 fixture failure, not an additional regression attributed to this PR. Draft #6664 separately fixes that fixture and its native7 hosted test passes; that does not change the result of this run. I did not run a native desktop or the complete Rust suite.


Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

if (fence && fence[1][0] === fenced[0] && fence[1].length >= fenced.length) fenced = null;
out.push(line);
index += 1;
continue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve fenced code after a non-closing backtick line

This closing check accepts a matching run with arbitrary trailing text. The complete input below is one valid GFM code block:

```md
alpha
``` not a closing fence
**原文:**继续
```

The third line cannot close the block because it has non-whitespace after the backticks. Nevertheless it clears fenced, so the fourth line is rewritten to **原文**:继续. Running the before/after through markdown-rs 1.0.0 with GFM options confirms the literal code content changes. Check the entire closing-fence line (including allowed trailing whitespace), or preserve the parser's code nodes before rewriting prose.

@M4n5ter M4n5ter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

English

Reviewed d34a576f3. Scoping this normalization to BotReply.tsx (completed SendUser replies) while leaving streaming MarkdownBody.tsx untouched makes sense given TextView's current.starts_with(old) prefix-streaming path.

Before this can land, there are four concrete issues in the block filter, punctuation movement, delimiter/fence matching, and tests that need to be addressed:

1. Whole-block PLAIN_PARAGRAPH / indentedCode / hasAddress bailout disables the fix on most real Bot replies (products/maka/workbench/app/markdownEmphasis.ts:65-67, 96-111)

  • closeCjkEmphasis groups all contiguous non-blank, non-fence lines into a single block (lines 96-105) and skips the entire block whenever any character in ^[^`<>\\\[\]]*$ fails, any URL appears, or any line has 4 leading spaces (lines 106-110).
  • In real SendUser summaries, this leaves the exact target pattern (- **回升能否守住:**如果…) unfixed across standard Markdown structures:
    • Tight lists with inline code or links in any item (disables every item in the list, even plain ones):
      • - **回升能否守住:**如果很快跌回 `3200` 下方则偏弱\n- **量能配合:**成交额需持续放大 -> unchanged
      • - **回升能否守住:**如果很快跌回\n- **参考来源:**详见 [日报](https://example.com) -> unchanged
    • Nested list items indented by 4 spaces (misclassified as indentedCode at line 67):
      • - **主线方向:**关注半导体\n - **细分环节:**设备与材料优先 -> unchanged
    • Blockquotes / GFM alerts, task lists, GFM tables with code/links, and prose with < / > comparisons:
      • > **结论:**短线观望为主 -> unchanged
      • - [x] **第一步:**已完成配置 -> unchanged
      • **回撤控制:**若单日跌幅 >5% 则减仓 -> unchanged
  • Instead of rejecting entire multi-line blocks on special characters, scan past actual Markdown constructs (fenced/indented code blocks, matching-length inline backtick spans, link/image destinations & reference definitions, autolinks/HTML tags, and backslash escapes) and rewrite only prose text segments.

2. Unconditional edge-punctuation stripping breaks balanced quotes/brackets and rips inner units/closers out of bold (products/maka/workbench/app/markdownEmphasis.ts:31-32, 47-55)

  • MOVABLE (lines 31-32) matches every Unicode [\p{P}\p{S}] character, and fixPairs (lines 47-51) pops all trailing/leading MOVABLE characters on the blocked side without checking paired delimiters or inner units:
    • Parenthesized phrases & units: **营收(同比 +12%)**增长明显 is rewritten to **营收(同比 +12**%)增长明显 — both ) and % (\p{P}) are stripped outside **, leaving ( bold inside <strong> and %) unbolded outside.
    • One-sided quote/bracket stripping:
      • 他说**“不行”**。详见**《操作指南》**,当前状态**【正常】** -> 他说“**不行”**。详见《**操作指南》**,当前状态【**正常】** (opening “ / 《 / 【 moves outside while closing ” / 》 / 】 stays bold).
      • 这是 **“核心指标”**需要重点关注 -> 这是 **“核心指标**”需要重点关注 (opening “ stays bold while closing ” moves outside).
  • Moving trailing clause/label punctuation (:, ,, 。, ;, :, etc.) should not split balanced brackets/quotes ((), “”, ‘’, 《》, 【】, 「」, 『』, (), []) or peel units like % out of a parenthesized span; if a paired wrapper wraps the whole bold content and only one side is blocked, keep the pair balanced rather than half-bolding it.

3. fixPairs delimiter pairing and fenced tracking violate CommonMark rules and corrupt prose or code (products/maka/workbench/app/markdownEmphasis.ts:40-56, 78-90, 99-102)

  • Non-opening / non-closing ** mis-pairing (line 40): /\*\*([^*\n]+?)\*\*/gu pairs any two ** on a line without checking CommonMark flank rules on inner itself (an opener cannot be followed by whitespace; a closer cannot be preceded by whitespace). In plain prose such as 计算 2 ** 3 之后,**结论:**成立, it pairs the literal ** in 2 ** 3 with the opening ** of **结论:**, corrupting the sentence into 计算 2 ** 3 之后**,结论:**成立.
  • Fence opening/closing rules (lines 78-90, 101, CommonMark §4.5):
    • A closing fence may only be followed by optional spaces/tabs to end of line. At line 80, an inner ```python line inside an outer ```markdown fence clears fenced = null, causing subsequent code lines like **重点:**这是代码示例,不应被改写 inside the still-open fence to be mutated into **重点**:这是代码示例,不应被改写.
    • A backtick opening fence's info string cannot contain backticks. A paragraph starting with inline triple-backtick code (```foo``` 是内联代码) matches fence at line 85, sets fenced = "```", and disables rewriting for the rest of the reply.

4. Tests lock in the whole-paragraph bailout instead of desired user-visible behavior (products/maka/workbench/tests/markdown-emphasis.test.mjs:54-88)

  • Assertions at lines 55-57, 75-77, and 84-87 explicitly require **回升:**如果, **甲:**乙, and **丙:**丁 to stay broken whenever the same paragraph also contains `代码`, <b>丙</b>, \*, or ![图](a.png).
  • Once prose scanning skips code/links/HTML/escapes directly, update those tests to assert that inline code, URLs, HTML, and escapes remain byte-identical while adjacent CJK bold spans in the same paragraph/list close properly, and add regression cases for **营收(同比 +12%)**增长明显, balanced **“…”** / **《…》**, 2 ** 3 before **结论:**成立, and nested fenced examples.
简体中文

看完了 d34a576f3。将这个规范化限定在 BotReply.tsx(已完成的 SendUser 回复)而不改动流式 MarkdownBody.tsx 是合理的,不会破坏 TextView 流式渲染依赖的 current.starts_with(old) 前缀快路径。

在合并前,块级过滤、标点外移、分隔符/围栏匹配以及测试断言上有 4 个可稳定复现的问题需要处理:

1. 整段 PLAIN_PARAGRAPH / indentedCode / hasAddress 放弃策略导致真实 Bot 回复大面积失效(products/maka/workbench/app/markdownEmphasis.ts:65-67, 96-111)

  • closeCjkEmphasis 会把连续非空、非围栏行合并为一个 block(lines 96-105),只要块内出现任何 ` < > \ [ ] 字符、任意 URL 或任意 4 空格缩进行,就整块跳过(lines 106-110)。
  • 在真实的 Bot 总结回复中,这会导致目标场景(- **回升能否守住:**如果…)在常见 Markdown 结构下完全不生效:
    • 紧凑列表中任意一项带行内代码或链接(会连带导致整个列表的所有普通项全部失效):
      • - **回升能否守住:**如果很快跌回 `3200` 下方则偏弱\n- **量能配合:**成交额需持续放大 -> 完全未处理
      • - **回升能否守住:**如果很快跌回\n- **参考来源:**详见 [日报](https://example.com) -> 完全未处理
    • 4 空格缩进的嵌套子列表(在 line 67 被误判为 indentedCode):
      • - **主线方向:**关注半导体\n - **细分环节:**设备与材料优先 -> 完全未处理
    • 引用块 / GFM 提示块、任务列表、含代码/链接的表格,以及带 < / > 比较符的正文:
      • > **结论:**短线观望为主 -> 完全未处理
      • - [x] **第一步:**已完成配置 -> 完全未处理
      • **回撤控制:**若单日跌幅 >5% 则减仓 -> 完全未处理
  • 请不要用特殊字符黑名单整段放弃;应扫描跳过真正的 Markdown 非散文结构(围栏/缩进代码块、匹配反引号长度的行内代码、链接/图片目标与引用定义、自动链接/HTML 标签、反斜杠转义),仅对散文片段做规范化。

2. 无条件剥离边缘标点会拆裂成对引号/括号,并把括号内的单位与右括号拆出粗体(products/maka/workbench/app/markdownEmphasis.ts:31-32, 47-55)

  • MOVABLE(lines 31-32)匹配所有 Unicode [\p{P}\p{S}],而 fixPairs(lines 47-51)在受阻一侧循环弹出所有 MOVABLE 字符,没有考虑成对定界符和内部单位:
    • 带括号补充说明与百分号:**营收(同比 +12%)**增长明显 会被改写成 **营收(同比 +12**%)增长明显——) 和 %(属于 \p{P})都被移出 **,导致左括号 ( 留在粗体内,而 %) 掉到粗体外。
    • 成对引号/书名号/方头括号被单侧拆裂:
      • 他说**“不行”**。详见**《操作指南》**,当前状态**【正常】** -> 他说“**不行”**。详见《**操作指南》**,当前状态【**正常】**(左侧 “ / 《 / 【 被移出粗体,右侧 ” / 》 / 】 仍留在粗体内)。
      • 这是 **“核心指标”**需要重点关注 -> 这是 **“核心指标**”需要重点关注(左引号留在粗体内,右引号被移出)。
  • 外移句读/冒号类标点(如 :、、、,、。、;、:)时不应拆散成对括号/引号(()、“”、‘’、《》、【】、「」、『』、()、[])或把 % 等单位从括号短语中剥出来;若成对引号/括号整体包裹加粗词且仅单侧受阻,也应保持成对对称,避免出现半边加粗。

3. fixPairs 的 ** 配对与 fenced 围栏判定违反 CommonMark 规则,会改坏正文或代码块内容(products/maka/workbench/app/markdownEmphasis.ts:40-56, 78-90, 99-102)

  • 非开启/非闭合 ** 错配污染正文(line 40):/\*\*([^*\n]+?)\*\*/gu 未检查 inner 首尾是否满足 CommonMark 开启/闭合前置条件(开启 ** 后不能紧跟空白,闭合 ** 前不能紧跟空白)。遇到 计算 2 ** 3 之后,**结论:**成立 这类普通文本时,会把 2 ** 3 里的字面量 ** 与后面 **结论:** 的左 ** 配成一对,将文本改坏为 计算 2 ** 3 之后**,结论:**成立。
  • 代码块围栏开启/闭合判定不符合 CommonMark §4.5(lines 78-90, 101):
    • 闭合围栏后只能跟可选空格/Tab 到行尾。line 80 未检查行尾内容,导致外层 ```markdown 代码块中出现 ```python 示例行时会提前把 fenced 清为 null,后续仍处于代码块内的字面量 **重点:**这是代码示例,不应被改写 会被错误改写为 **重点**:这是代码示例,不应被改写。
    • 反引号开启围栏的 info string 不能包含反引号。行首行内代码(如 ```foo``` 是内联代码)会在 line 85 命中 fence 并将 fenced 置为 "```",导致后文所有段落全部停止处理。

4. 测试用例锁定了“整段跳过”的临时实现,而非期望的用户可见行为(products/maka/workbench/tests/markdown-emphasis.test.mjs:54-88)

  • lines 55-57、75-77、84-87 明确断言:只要同一段里含有 `代码`、<b>丙</b>、\* 或 ![图](a.png),旁边的 **回升:**如果、**甲:**乙、**丙:**丁 就必须保持未闭合的原样。
  • 改为跳过非散文结构后,这些用例应断言行内代码、URL、HTML 与转义内容保持字节级不变,同时同段/同列表中的中文加粗正常闭合;并补充 **营收(同比 +12%)**增长明显、成对 **“…”** / **《…》**、2 ** 3 接 **结论:**成立 以及嵌套代码块围栏的回归测试。

liugddx and others added 2 commits November 6, 2026 17:15
…skipping whole paragraphs

Review of #6658. The paragraph-level bail-out left the fix off in common Bot replies:
any list item with code or a link, any nested list item, quotes, task lists and prose
with `<` or `>` were all skipped. It also split balanced quotes and brackets, paired a
literal `**` such as `2 ** 3` with a real opener, and mis-tracked fences.

- Protected spans are found first (escapes, code spans of matching length including
  across lines, link and image destinations, reference definitions, autolinks, HTML
  tags, raw URLs) and only the prose around them is rewritten.
- `**` runs are paired as CommonMark would: an opener cannot be followed by blank text,
  a closer cannot follow it, so `2 ** 3` is ignored.
- A balanced wrapper around the bold text moves out as a pair; only clause punctuation
  is peeled alone. A bracket that closes inside the bold text, or a unit such as `%`,
  is never separated: the pair is left as written.
- Fences follow CommonMark: the closing line may carry only blanks after its run, a
  backtick fence's info string cannot contain a backtick, and a shorter run does not
  close a longer one. Four-space indentation is code unless it starts a list item.

Generated-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the complete three-file change at bb1a321 and compared the new scanner with the previously reviewed version. All ten committed tests pass in my Node 22 run. The earlier trailing-text fence counterexample is now preserved, as are the previous link destinations and inline-code examples; I confirmed those results using the actual markdown-rs 1.0.0 GFM parser.

P2 — multiline indented code is still rewritten (markdownEmphasis.ts:249–254). The indented-code check only recognizes a line immediately after a blank, then sets previousBlank = false. Consequently, the next indented line is collected as prose. For the complete input first line\n **原文:**继续, GFM produces one code block, but this function changes its second line to **原文**:继续. I parsed both before and after and confirmed the code node changes. A blank line inside the code block produces the same problem after the next preserved line. Keep the indented-code block state until its actual end, or preserve these blocks through the parser, rather than deciding each line only from the preceding blank flag.

The new raw-HTML handling also protects tags without tracking a <pre> block's body across blank lines; my parser comparison shows that body changes as well. This is the same unresolved requirement to preserve non-prose content, rather than a separate severity claim. The twelve-input before/after probe includes both cases, the old fence regression and unchanged controls.

The changes merge cleanly with rust-preview@01115825. Current CI is still running; I am not treating the old d34a run's inherited fixture failure as this head's outcome. No protocol or database migration changes. I did not run a native desktop or the complete workbench/Rust suite; the author's 34-input program and 115 workbench tests are not my results.


Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

}
// Four spaces after a blank line is code unless it starts a (nested) list item.
const indentedCode =
previousBlank && /^(?: {4,}|\t)/u.test(line) && !/^\s*(?:[-*+]|\d+[.)])\s/u.test(line);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve every line of an indented code block

For the complete input first line\n **原文:**继续, the first line is recognized as code, but setting previousBlank to false makes the second line fail this check. It reaches fixParagraph and becomes **原文**:继续. Actual markdown-rs 1.0.0 GFM parsing confirms both inputs are a single indented code block and its literal contents differ. The same issue recurs after blank lines within an indented block. Track the block context through its end, rather than protecting just its first line.

@M4n5ter
M4n5ter merged commit 4c2bb68 into rust-preview Nov 6, 2026
@M4n5ter
M4n5ter deleted the fix/bot-markdown-cjk-emphasis branch November 6, 2026 11:38

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rechecked the final three-file change at 0243b66 after the latest indented-code fix. The ordinary multiline indented block and the block containing a blank line now remain byte-for-byte unchanged; the earlier URL, inline-code and fence regressions also remain fixed in my checks. The 11 committed Markdown-emphasis tests pass locally.

P2: quoted indented code is still rewritten. In markdownEmphasis.ts:313–319, indentation is measured before removing blockquote markers. For example:

>     first line
>     **原文:**继续

The normalizer changes the second line to > **原文**:继续. I ran the actual normalizer and the workbench's markdown 1.0.0 GFM parser: the content is parsed into code blocks inside a blockquote, and the literal code body changes. Bot replies can therefore display or copy different code from the supplied source. Please identify container-relative code blocks before applying the emphasis rewrite. Raw HTML pre blocks containing **原文:**继续 between blank lines also still have their literal body rewritten, as in the preceding review; the new indented-block state does not address that part of the earlier finding.

I read the complete latest delta, separately from the changed base, and ran 13 before/after parser cases including the intended strong-emphasis example. This is a source/real-parser check, not a native UI acceptance test. This PR has already been merged. Its final-head CI run 42211700573 remains incomplete; the old bb1a run's two lifecycle/send waits are not evidence about the final head. No protocol or schema changes, approval or merge performed.


Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

const { cols, offset } = lineIndent(line);
while (listStack.length > 0 && listStack.at(-1)! > cols) listStack.pop();
const baseIndent = listStack.at(-1) ?? 0;
if (paragraph.length === 0 && cols >= baseIndent + 4) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 — preserve container-relative indented code before rewriting emphasis.

For > first line\n> **原文:**继续, lineIndent(line) sees zero columns because it runs before the > container prefix is removed. This branch never enters code mode, and the literal code becomes **原文**:继续. I reproduced the changed code body with the actual normalizer and the workbench’s markdown 1.0.0 GFM parser. Strip/track the block container before identifying indented code; ordinary top-level indented blocks are now protected.


Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/M Under 500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants