Skip to content

Conversation

@Last-Order
Copy link

@Last-Order Last-Order commented Dec 26, 2025

中文版模板 / Chinese template

🤔 This is a ...

  • 🆕 New feature
  • 🐞 Bug fix
  • 📝 Site / documentation improvement
  • 📽️ Demo improvement
  • 💄 Component style improvement
  • 🤖 TypeScript definition improvement
  • 📦 Bundle size optimization
  • ⚡️ Performance optimization
  • ⭐️ Feature enhancement
  • 🌐 Internationalization
  • 🛠 Refactoring
  • 🎨 Code style optimization
  • ✅ Test Case
  • 🔀 Branch merge
  • ⏩ Workflow
  • ⌨️ Accessibility improvement
  • ❓ Other (about what?)

🔗 Related Issues

💡 Background and Solution

  1. Fixed stuck "loading" state for void elements:

    detectUnclosedTags now correctly handles void elements (e.g., img). The parser now supports:

    • Standard HTML: <img src="..."> (No end tag)

    • Self-closing (XHTML/JSX style): <img src="..." />

    • Explicit closing (Non-standard): <img src="..."></img>

    Fixed the bug where registering a custom img component resulted in a perpetual loading streamStatus due to the tag being perceived as unclosed.

  2. Fixed shared state for multiple components:

    Resolved an issue in the renderer where multiple custom components within the same Markdown block shared a single streamStatus.

    Status is now tracked independently per component instance.

  3. Added related tests.

📝 Change Log

Language Changelog
🇺🇸 English Fix the issue where void elements (e.g., img) are assigned an incorrect streamStatus, and correct the shared stream status for multiple component instances.
🇨🇳 Chinese 修复 HTML 标准中的 void elements(如 img)的自定义组件获得的 streamStatus 不正确的问题,并修正了多个组件实例共享流式状态的问题。

Summary by CodeRabbit

发布说明

  • 新功能

    • 改进了HTML标签检测机制,增强了对未关闭标签的精确处理。
    • 优化了自定义组件在流式渲染中的支持和状态跟踪。
  • Bug修复

    • 修复了多个相同标签的处理问题,提升了复杂HTML结构的兼容性。
  • 测试

    • 扩展了测试覆盖范围,新增嵌套场景和边界情况的测试。

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings December 26, 2025 10:23
@github-actions
Copy link
Contributor

github-actions bot commented Dec 26, 2025

Preview failed

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 26, 2025

📝 Walkthrough

概览

引入了基于位置的手动HTML解析器替代正则表达式方法,重构了未闭合标签检测机制。新增per-tag计数器和状态追踪,支持区分同一标签的多个实例,并扩展渲染流程以通过标签索引传播流状态信息。

变更内容

分组 / 文件 变更摘要
HTML解析器重构
packages/x-markdown/src/XMarkdown/core/Renderer.ts
新增WHITESPACE_REGEXTAG_NAME_CHAR_REGEXVOID_ELEMENTS常量。使用手动、基于位置的HTML解析器替代正则表达式未闭合标签检测。引入per-instance tagIndexes状态和tag-index后缀记录(如tagname-1)。更新createReplaceElementprocessChildren方法签名以支持扩展的cidRef对象,包含tagIndexes映射。调整DOMPurify集成以适配新的标签索引机制。
测试套件更新
packages/x-markdown/src/XMarkdown/__tests__/Renderer.test.ts
更新detectUnclosedTags测试预期从纯标签名改为带后缀的键(如tag-1)。添加大量新测试用例覆盖void元素、嵌套/混合组件实例、streamStatus传播和复杂嵌套场景。多个现有测试调整为使用size检查和后缀存在性检查。新增测试验证DOMPurify配置、class/className合并和组件层次保留。

代码审查工作量评估

🎯 4 (复杂) | ⏱️ ~60 分钟

诗歌

🐰 啃啃笔尖出聪慧
位置追踪代正则
每tag一索映实例
嵌套混乱化井然
流状态随风而行

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确总结了主要变更:修复void元素处理和隔离组件流状态。内容与changeset中的核心改动相符,简洁且具体。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dosubot dosubot bot added the bug Something isn't working label Dec 26, 2025
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Last-Order, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses two critical bugs within the XMarkdown renderer. It significantly improves the HTML parsing logic to accurately recognize and process void elements and self-closing tags, thereby preventing custom components associated with these elements from entering a perpetual loading state. Furthermore, it refactors the streamStatus management to ensure that each instance of a custom component maintains its own independent loading status, eliminating issues caused by shared states across multiple components.

Highlights

  • Void Element Handling: The detectUnclosedTags logic has been enhanced to correctly identify and handle void elements (e.g., <img>) and self-closing tags (<tag />). This prevents custom components registered for these tags from being incorrectly perceived as unclosed, resolving a bug where they would get stuck in a 'loading' state.
  • Isolated Component Stream Status: The streamStatus for custom components is now tracked independently for each component instance. Previously, multiple custom components within the same Markdown block could share a single streamStatus, leading to incorrect loading states. This change ensures each component manages its own status.
  • Improved HTML Parsing: The internal HTML parsing mechanism in detectUnclosedTags has been rewritten from a regex-based approach to a more robust manual string parsing, allowing for more precise detection of tag states, including handling attributes and various closing tag syntaxes.
  • New Test Cases: Comprehensive test cases have been added to validate the correct handling of void elements and the new instance-based streamStatus tracking, ensuring the reliability of these fixes.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively addresses two important bugs: the incorrect handling of void elements causing a perpetual loading state, and the shared stream status among multiple component instances. The new implementation of detectUnclosedTags is a significant improvement, providing a more robust parsing mechanism that correctly identifies unclosed tags while isolating component instances. The addition of VOID_ELEMENTS and the instance-based tracking (tag-name-N) are clever solutions. The accompanying tests are thorough and cover the new logic and edge cases well. I have a few suggestions to improve the maintainability of the new, more complex parsing logic.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (1)

42-167: 重构后的 detectUnclosedTags 逻辑清晰,能正确处理多实例场景。

新实现有以下优点:

  1. 使用位置解析而非正则,更可靠地处理引号内的 > 字符
  2. 每个标签类型独立计数,生成唯一标识符(如 div-1div-2
  3. 正确处理 void 元素、自闭合语法和大小写不敏感匹配

建议移除第 160 行的注释代码:

🔎 建议的修改
    for (const tagNameLower of tagsToCheckLower) {
      if (openTagsStack[tagNameLower]) {
        for (const tagIndex of openTagsStack[tagNameLower]) {
-          // unclosedTags.add(`${tagNameLower}`);
          unclosedTags.add(`${tagNameLower}-${tagIndex}`);
        }
      }
    }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7004f2b and 68188e0.

📒 Files selected for processing (2)
  • packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts
  • packages/x-markdown/src/XMarkdown/core/Renderer.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts (1)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (1)
  • detectUnclosedTags (42-167)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (1)
packages/x-markdown/src/XMarkdown/core/index.ts (1)
  • Renderer (4-4)
🪛 GitHub Actions: ✅ test
packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts

[error] 1157-1157: TS1005: '}' expected.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Agent
  • GitHub Check: build preview
  • GitHub Check: size
🔇 Additional comments (7)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (3)

18-33: LGTM! 标准 HTML void 元素集合定义正确。

该集合正确包含了所有 HTML5 规范中的 void 元素,用于正确处理 <img> 等自闭合标签的流状态判断。


186-189: LGTM! tagIndexes 机制正确隔离了组件实例的流状态。

通过在 cidRef 中维护 tagIndexes 映射,每个自定义组件实例现在独立跟踪其索引,与 detectUnclosedTags 生成的唯一标识符(tagName-index)正确匹配,解决了多个同名组件共享流状态的问题。

DOM 遍历顺序(深度优先)与标签检测顺序一致,确保索引匹配正确。

Also applies to: 210-214


259-259: LGTM! cidRef 初始化扩展正确。

添加 tagIndexes: {} 为每次 processHtml 调用提供独立的标签索引状态。

packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts (4)

73-89: 新增的 void 元素测试用例覆盖良好。

测试正确验证了:

  1. 标准 void 元素语法 <img src="..."> 不被视为未闭合
  2. 自闭合语法 <img src="..." /> 同样正确处理
  3. 不完整的标签(缺少 >)被正确检测为未闭合

修复上述语法错误后,这些测试将有效验证 void 元素的处理逻辑。


25-35: 测试期望已正确更新为后缀格式。

更新后的断言(如 'custom-tag-1''tag-b-1')与 Renderer.ts 中新的按标签索引机制保持一致。

Also applies to: 52-54


587-588: Mock 数据已正确更新为后缀格式。

mockUnclosedTags 使用 'streaming-tag-1' 格式与新的标签索引机制匹配,确保 streamStatus 集成测试的有效性。


990-991: 所有 mock 未闭合标签集合已统一使用新的后缀命名格式。

unclosed tags scenarios 测试中的 mock 数据(如 'div-1''span-1''p-1''a-1')正确反映了新的按实例索引机制,能有效验证嵌套结构中各组件的 streamStatus 独立性。

Also applies to: 1026-1027, 1063-1064, 1097-1098

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes two critical bugs in the XMarkdown component's streaming functionality:

  1. Void element handling: Corrects detection of void HTML elements (like <img>) to prevent perpetual "loading" status
  2. Component stream status isolation: Ensures multiple instances of the same custom component receive independent stream status tracking

Key Changes

  • Replaced regex-based tag parsing with character-by-character scanning for more robust HTML parsing
  • Added VOID_ELEMENTS constant containing all HTML5 void elements
  • Implemented per-instance stream status tracking using indexed identifiers (tag-1, tag-2, etc.)

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
packages/x-markdown/src/XMarkdown/core/Renderer.ts Rewrote detectUnclosedTags method with manual parsing, added VOID_ELEMENTS set, modified stream status tracking to use indexed tag identifiers for per-instance isolation
packages/x-markdown/src/XMarkdown/test/Renderer.test.ts Updated test assertions to expect indexed tag identifiers (tag-1, tag-2) instead of plain tag names, added void element test case

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (1)

42-166: detectUnclosedTags 实现功能正确但建议重构以提高可维护性。

新的基于位置的解析器正确实现了所有关键特性:

  • 大小写不敏感的标签匹配(第 46-48 行)
  • 按出现次序生成唯一标识符(第 50-51、107-108 行)
  • 空元素识别(第 138 行)
  • 自闭合语法检测(第 118-121 行)

然而,之前的评审意见仍然有效:该方法的长度和复杂度已显著增加。

🔎 建议的重构

考虑将解析逻辑拆分为更小的辅助方法:

  • parseOpeningTag(...) - 提取开始标签的解析逻辑
  • parseClosingTag(...) - 提取结束标签的解析逻辑
  • 将正则表达式 /\s//[a-zA-Z0-9-]/ 定义为类的静态只读属性

这将简化主 while 循环,提高代码的可读性和可测试性。

基于之前的评审反馈,建议进行重构以提高可维护性。

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68188e0 and e1b7dd4.

📒 Files selected for processing (2)
  • packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts
  • packages/x-markdown/src/XMarkdown/core/Renderer.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (1)
packages/x-markdown/src/XMarkdown/core/index.ts (1)
  • Renderer (4-4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test
  • GitHub Check: build preview
  • GitHub Check: size
🔇 Additional comments (13)
packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts (9)

12-36: 测试用例正确反映了新的标签索引逻辑。

期望值已更新为使用带后缀的标识符(custom-tag-1),这与 Renderer.ts 中新的 detectUnclosedTags 实现一致。测试正确验证了未闭合标签、已闭合标签和自闭合标签的行为。


38-55: 多标签场景的测试更新正确。

测试正确验证了当一个标签已闭合(tag-a)而另一个标签未闭合(tag-b)时,只有 tag-b-1 会出现在结果集中。这与新的按出现次序生成后缀标识的逻辑一致。


73-89: 优秀的空元素测试覆盖。

这个新增的测试用例很好地验证了 PR 的核心修复:

  • 正确处理标准 HTML 空元素语法(<img src="...">
  • 正确处理自闭合语法(<img src="..." />
  • 正确处理不完整的标签(<img src="image.p)仍被标记为未闭合

测试覆盖了空元素处理的关键场景。


444-474: 嵌套标签边界情况测试已正确更新。

测试用例准确验证了:

  • 内层标签未闭合时,只有 inner-tag-1 被检测到
  • 外层标签未闭合时,只有 outer-tag-1 被检测到

这确保了新的解析器能够正确跟踪嵌套结构中的标签状态。


476-494: 大小写不敏感测试已正确更新。

测试验证了标签名的大小写不敏感处理,期望值正确使用了小写的后缀标识符(test-tag-1),与 Renderer.ts 中将标签名转换为小写的实现一致。


496-541: 属性和畸形 HTML 的边界测试已正确更新。

这些测试用例覆盖了重要的边界情况:

  • 带属性的标签(class、id 等)
  • 畸形的闭合标签(缺少 >
  • 多余的闭合标签

所有期望值都已更新为使用后缀标识符,确保解析器在各种边界条件下的正确性。


557-571: 关键测试:验证了同一标签多次出现的索引机制。

此测试直接验证了 PR 的核心目标之一:当同一标签多次出现时,每个实例都有独立的索引(第二个未闭合的 custom-tag 被标识为 custom-tag-2)。这是实现多个自定义组件独立 streamStatus 的基础。


574-630: streamStatus 集成测试已正确更新。

Mock 已更新为使用后缀标识符(streaming-tag-1),正确验证了:

  • 未闭合标签的 streamStatus'loading'
  • 已闭合标签的 streamStatus'done'

这确保了新的标签索引机制与流式渲染状态正确集成。


978-1117: 优秀的多组件独立 streamStatus 测试覆盖。

这些测试用例直接解决了之前 Copilot 提出的测试覆盖缺口:验证同一 Markdown 块中的多个自定义组件接收独立的 streamStatus 值。测试场景包括:

  • 父标签未闭合、子标签已闭合:父组件 streamStatus: 'loading',子组件 streamStatus: 'done'
  • 父标签已闭合、子标签未闭合:相反的状态分配
  • 多层级多个未闭合标签:每个组件根据其自身的闭合状态接收正确的状态

所有 mock 都正确使用了后缀标识符(div-1span-1 等),完美验证了 PR 的核心修复目标。

基于之前的评审反馈,此部分测试覆盖了关键场景。

packages/x-markdown/src/XMarkdown/core/Renderer.ts (4)

18-33: 正确定义了 HTML5 空元素集合。

VOID_ELEMENTS 静态集合包含了所有标准的 HTML5 空元素(area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr),这是正确处理自闭合标签的基础。实现为 static readonly 确保了性能和不可变性。


185-244: 关键修复:组件流式状态隔离实现。

createReplaceElement 方法中的更新实现了 PR 的核心目标:

  • 第 210 行:为每个自定义组件维护独立的索引计数器(cidRef.tagIndexes[name]
  • 第 211-213 行:使用带索引的标识符(${name}-${cidRef.tagIndexes[name]})来查询该特定实例的流式状态

这确保了同一 Markdown 块中的多个相同类型组件不再共享单一的 streamStatus,每个实例根据其自身的闭合状态独立接收 'loading''done' 状态。


246-254: processChildren 正确传播 tagIndexes 状态。

方法签名更新以包含 tagIndexes(第 249 行),确保在处理嵌套子元素时标签索引状态能够正确传播。这对于维护跨整个组件树的一致索引至关重要。


256-267: processHtml 正确初始化标签索引跟踪。

第 258 行将 cidRef 初始化为包含空的 tagIndexes 对象,为每次 HTML 处理提供了干净的起始状态。这确保了不同渲染调用之间的索引计数器不会相互干扰。

@codecov
Copy link

codecov bot commented Dec 26, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.13%. Comparing base (86b380e) to head (4707677).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1590      +/-   ##
==========================================
+ Coverage   97.09%   97.13%   +0.03%     
==========================================
  Files         140      140              
  Lines        4273     4323      +50     
  Branches     1203     1210       +7     
==========================================
+ Hits         4149     4199      +50     
  Misses        122      122              
  Partials        2        2              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codecov
Copy link

codecov bot commented Dec 26, 2025

Bundle Report

Changes will increase total bundle size by 855 bytes (0.05%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
x-markdown-array-push 107.49kB 855 bytes (0.8%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: x-markdown-array-push

Assets Changed:

Asset Name Size Change Total Size Change (%)
x-markdown.min.js 855 bytes 104.75kB 0.82%

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (1)

248-260: 考虑提取 cidRef 类型定义以提升可维护性。

cidRef 参数类型 { current: number; tagIndexes: Record<string, number> } 在多处重复定义。可以考虑将其提取为命名类型,提升代码可读性和一致性。

🔎 建议的重构
+interface CidRef {
+  current: number;
+  tagIndexes: Record<string, number>;
+}

-  private createReplaceElement(
-    unclosedTags: Set<string> | undefined,
-    cidRef: { current: number; tagIndexes: Record<string, number> },
-  ) {
+  private createReplaceElement(
+    unclosedTags: Set<string> | undefined,
+    cidRef: CidRef,
+  ) {

-  private processChildren(
-    children: DOMNode[],
-    unclosedTags: Set<string> | undefined,
-    cidRef: { current: number; tagIndexes: Record<string, number> },
-  ): ReactNode {
+  private processChildren(
+    children: DOMNode[],
+    unclosedTags: Set<string> | undefined,
+    cidRef: CidRef,
+  ): ReactNode {
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1b7dd4 and bb578ad.

📒 Files selected for processing (2)
  • packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts
  • packages/x-markdown/src/XMarkdown/core/Renderer.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts (1)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (1)
  • detectUnclosedTags (44-168)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build preview
  • GitHub Check: test
  • GitHub Check: size
🔇 Additional comments (7)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (3)

18-35: LGTM!静态常量定义清晰且完整。

VOID_ELEMENTS 包含了所有 HTML5 规范中的空元素,WHITESPACE_REGEXTAG_NAME_CHAR_REGEX 遵循了之前评审中关于集中管理正则表达式的建议。


44-168: 实现逻辑正确,有效解决了 void 元素和多实例流状态问题。

手动解析方式比正则更健壮,基于栈的追踪机制正确处理了嵌套标签。使用 tagname-index 后缀格式使每个组件实例能独立追踪流状态,这正是 PR 描述中提到的核心修复。

有一个小细节可以考虑:当前实现跳过了无效的标签语法(如 < 后直接跟数字的情况),这是合理的容错行为。


211-215: 流状态独立追踪逻辑正确。

通过 cidRef.tagIndexes 为每种标签类型维护递增计数器,并使用 ${name}-${cidRef.tagIndexes[name]} 格式查找未闭合状态,确保了同一 Markdown 块中多个相同组件实例能获得独立的 streamStatus。这正是 PR 描述中要解决的核心问题。

packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts (4)

73-96: void 元素测试用例覆盖全面。

测试覆盖了三种关键场景:

  1. 标准 void 元素形式 (<img src="...">)
  2. 自闭合形式 (<img src="..." />)
  3. 不完整的语法 (<img src="image.p)

这与 PR 描述中的修复内容完全对应。


1022-1055: 此测试有效验证了组件实例独立流状态的修复。

通过模拟 span-1 为未闭合状态,验证了父组件 div 获得 done 状态而子组件 span 获得 loading 状态。这正好覆盖了之前评审中提到的"同一 Markdown 块中多个自定义组件共享单一 streamStatus"的场景。


564-578: 多实例场景测试正确验证了索引机制。

测试用例 <custom-tag>first</custom-tag><custom-tag>second 验证了第一个标签闭合后,第二个未闭合标签被标识为 custom-tag-2,确认了标签计数器的正确递增行为。


1-9: 测试文件更新完整,覆盖了 PR 修复的核心场景。

测试用例已全面更新以适配新的后缀命名机制 (tagname-index),涵盖了:

  • void 元素处理
  • 多实例独立流状态
  • 嵌套结构
  • 边界情况

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts (1)

73-96: ✅ Void 元素测试覆盖全面,建议添加注释说明边缘情况。

新增的 void 元素测试正确覆盖了 PR 目标中提到的三种形式(标准 HTML、自闭合、显式闭合)。测试逻辑准确。

建议改进:

  • Line 88 的测试用例注释为 "Unclosed void element",但实际测试的是格式错误的标签(缺少 >)。建议添加注释说明这是在测试不完整/格式错误的标签解析。
  • Line 93 的 html3 包含未闭合的 <p> 标签但预期 size 为 0。建议添加注释说明 <p> 未注册为组件因此不被跟踪。
🔎 建议的注释改进
      // Test case: Unclosed void element
+     // Note: This tests an incomplete/malformed tag (missing closing >)
      const html2 = '<img src="image.p';
      const result2 = detectUnclosedTags(html2);
      expect(result2.has('img-1')).toBe(true);

      // Test case: Nested void elements
+     // Note: The unclosed <p> tag is not tracked because 'p' is not registered as a component
      const html3 = '<div><img src="image.png"></div><p>';
      const result3 = detectUnclosedTags(html3);
      expect(result3.size).toBe(0);
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb578ad and 68160fd.

📒 Files selected for processing (1)
  • packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts (1)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (1)
  • detectUnclosedTags (44-168)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build preview
  • GitHub Check: size
  • GitHub Check: test
🔇 Additional comments (3)
packages/x-markdown/src/XMarkdown/__test__/Renderer.test.ts (3)

213-245: ✅ 完美响应了历史评审意见和 PR 目标!

此测试直接验证了 PR 描述中的核心 bug 修复:"多个自定义组件在同一 Markdown 块内不再共享单个 streamStatus"。测试准确地覆盖了多个实例独立跟踪 streamStatus 的场景(第一个实例闭合为 'done',第二个未闭合为 'loading'),完全响应了历史评审评论中 Copilot 的要求。


247-347: ✅ 测试覆盖全面且质量高!

这组扩展测试全面覆盖了多实例场景的各种复杂情况:

  • 全部闭合的多实例
  • 嵌套实例的独立状态跟踪
  • 深度嵌套混合状态
  • 不同组件类型交错

特别是 Line 283-286 正确地考虑了 React 的渲染顺序(children 先于 parent 创建),Lines 332-344 通过 domNode.name 正确区分不同组件类型。测试逻辑严谨,边缘情况处理到位。


25-25: ✅ 后缀命名约定更新一致且正确。

所有测试期望和 Mock 数据已正确更新为使用后缀标识符格式(如 custom-tag-1img-1streaming-tag-1),与 Renderer.ts 中的新实现完全对齐。命名规则一致:<tag-name-lowercase>-<occurrence-index>

特别注意 Line 713 正确使用了 custom-tag-2 表示同一标签的第二次出现,这体现了对新命名规则的准确理解。

Also applies to: 52-54, 607-609, 632-632, 652-652, 673-673, 713-713, 731-731, 1134-1134, 1170-1170, 1207-1207, 1241-1241

@Last-Order
Copy link
Author

@kimteayon Just a friendly ping to see if you've had a chance to review this. Thanks!

1. Update detectUnclosedTags to correctly handle void elements (e.g., img).
2. Refactor the renderer to ensure streamStatus is independent for custom component instances with the same name.
@Last-Order Last-Order force-pushed the fix/handle-void-elements branch from 68160fd to 4707677 Compare January 7, 2026 18:03
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (2)

82-92: 跨闭合标签场景的行为考虑

当前实现使用栈来匹配开闭标签,但对于跨闭合场景(如 <a><b></a></b>),闭合标签会按 LIFO 顺序弹出同名标签的栈。这在 HTML 解析器的行为中是可接受的,但如果需要严格匹配,可以考虑验证闭合顺序。

对于当前 streaming 状态检测的用例,此行为应该是正确的。


211-215: 考虑添加显式小写处理以提高代码健壮性

代码当前工作正常,因为 html-react-parser 的底层库 htmlparser2 会自动将所有标签名称规范化为小写。detectUnclosedTags 存储的是小写键(如 custom-tag-1),而 domNode.name 来自 html-react-parser,也始终是小写的。因此查找能够正确工作。

不过,为了提高代码明确性和防守性,建议在 createReplaceElement 中显式添加小写处理,避免对 html-react-parser 行为的隐式依赖:

建议的优化
         // Manage tag indexes for custom components used for streaming status determination
-        cidRef.tagIndexes[name] = (cidRef.tagIndexes[name] ?? 0) + 1;
-        const streamStatus = unclosedTags?.has(`${name}-${cidRef.tagIndexes[name]}`)
+        const nameLower = name.toLowerCase();
+        cidRef.tagIndexes[nameLower] = (cidRef.tagIndexes[nameLower] ?? 0) + 1;
+        const streamStatus = unclosedTags?.has(`${nameLower}-${cidRef.tagIndexes[nameLower]}`)
           ? 'loading'
           : 'done';
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68160fd and 4707677.

📒 Files selected for processing (2)
  • packages/x-markdown/src/XMarkdown/__tests__/Renderer.test.ts
  • packages/x-markdown/src/XMarkdown/core/Renderer.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (1)
packages/x-markdown/src/XMarkdown/core/index.ts (1)
  • Renderer (4-4)
packages/x-markdown/src/XMarkdown/__tests__/Renderer.test.ts (1)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (1)
  • detectUnclosedTags (44-168)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test
  • GitHub Check: build preview
  • GitHub Check: size
🔇 Additional comments (9)
packages/x-markdown/src/XMarkdown/core/Renderer.ts (3)

18-35: LGTM!

常量定义清晰且遵循了已建立的模式。VOID_ELEMENTS 集合包含了所有标准 HTML5 void 元素,这正确地解决了 void 元素处理的问题。


248-256: LGTM!

processChildren 的类型签名正确更新以包含 tagIndexes,确保 cidRef 在渲染过程中正确传递。


258-261: LGTM!

processHtml 正确初始化了 cidRef,包含空的 tagIndexes 对象。初始化逻辑清晰简洁。

packages/x-markdown/src/XMarkdown/__tests__/Renderer.test.ts (6)

22-55: LGTM!

测试用例已正确更新以使用新的带后缀的标签键(如 'custom-tag-1')和基于 size 的检查,与 detectUnclosedTags 的新实现保持一致。


73-96: LGTM!

新增的 void 元素测试覆盖全面,包括:

  • 标准 HTML void 形式
  • 自闭合形式
  • 不完整标签(缺少 >
  • 嵌套在其他标签中的 void 元素

这些测试正确验证了 PR 目标中提到的 void 元素处理修复。


213-347: LGTM! 出色的测试覆盖

新增的多实例测试非常全面,涵盖了:

  • 同一组件的多个实例,部分关闭部分未关闭
  • 嵌套实例(正确记录了子组件先处理的行为)
  • 深度嵌套的混合状态
  • 不同组件类型的交错使用

这些测试很好地验证了每个组件实例独立跟踪 streamStatus 的新功能。


587-714: LGTM!

边缘情况测试已正确更新以匹配新的命名约定。特别是第 713 行的测试 'custom-tag-2' 正确验证了第二个实例被标记为未关闭的情况。


1121-1261: LGTM!

未关闭标签场景测试已正确更新,mock 数据使用了新的带后缀的命名约定(如 'div-1''span-1')。测试覆盖了多种复杂场景,包括父标签未关闭、子标签未关闭、多级未关闭等情况。


731-732: LGTM!

streamStatus 集成测试已正确更新以使用带后缀的标签键。

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant