Skip to content

chore: feature merge main#1930

Closed
kimteayon wants to merge 28 commits into
featurefrom
main
Closed

chore: feature merge main#1930
kimteayon wants to merge 28 commits into
featurefrom
main

Conversation

@kimteayon

Copy link
Copy Markdown
Collaborator

中文版模板 / 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

  • Describe the source of related requirements, such as links to relevant issue discussions.
  • For example: close #xxxx, fix #xxxx

💡 Background and Solution

  • The specific problem to be addressed.
  • List the final API implementation and usage if needed.
  • If there are UI/interaction changes, consider providing screenshots or GIFs.

📝 Change Log

Language Changelog
🇺🇸 English
🇨🇳 Chinese

Div627 and others added 28 commits March 12, 2026 21:05
…styles

Default tag styles are applied via descendant selectors on the `.x-markdown`
container (e.g. `.x-markdown ul`, `.x-markdown li`, `.x-markdown code`), so they
leak into elements rendered by custom components (such as antd `Collapse`) that
are mounted inside the container, polluting their styles.

Add a `disableDefaultStyles` prop:
- `true` disables all built-in tag styles
- an array (e.g. `['ul', 'ol', 'li']`) disables only the listed tags

Implemented purely at the view layer: the root container gets `x-md-disable-all`
or per-tag `x-md-disable-<tag>` classes, and each default rule in index.css is
gated behind `:not(.x-md-disable-all):not(.x-md-disable-<tag>)`. The descendant
selector strategy (so nested Markdown inherits defaults) is preserved; only the
opt-out is added. Parser/Renderer core is untouched.

closes #1908

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI's antd version types `fontSizeHeading1/2` tokens as `string | number`,
breaking `token.fontSizeHeading1 + 10` (TS2365) and `token.fontSizeHeading2 * 2`
(TS2362). Wrap with `Number()` so the arithmetic is valid regardless of the
token's declared type.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`list-style` on `ul > li` / `ol > li` was gated only by `:not(.x-md-disable-li)`,
so `disableDefaultStyles={['ul']}` left the bullets/numbers in place — users had
to also pass `'li'`. Also gate these selectors by the container's disable class
so disabling `ul`/`ol` removes their markers as expected, while keeping the `li`
clause (no regression) and ordered/unordered independence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ult-styles

feat(x-markdown): add disableDefaultStyles to opt out of default tag styles
Seed output with full input for non-streaming to prevent layout jitter.

Signed-off-by: Waiter <waitersong2012@gmail.com>
Signed-off-by: Waiter <waitersong2012@gmail.com>
happy-dom (>= 20.10.x) returns '' from the Node.prototype.nodeName getter
for element instances. DOMPurify reads nodeName via Node.prototype, so every
element was treated as an unknown tag and stripped (keeping only text),
causing 24 Renderer streamStatus tests to fail. Restore a spec-correct
nodeName getter on Node.prototype in the jest setup. Test-env only;
real browsers and jsdom are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The streaming refactor seeds non-streaming output synchronously on first
render, so XMarkdown now renders during SSR too. DOMPurify's default export
has no `sanitize` method without a `window`, so server pre-render crashed
with "sanitize is not a function". Return null from processHtml when there
is no DOM, letting the client hydrate — same as the pre-refactor server
behavior. No new deps, no XSS exposure (server emits nothing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Exercise the branch where DOMPurify.sanitize is unavailable (server pre-render)
by temporarily clearing it, asserting processHtml/render return null instead of
throwing. Raises patch coverage for the SSR guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(x-markdown): avoid jank when initial streaming output is non-streaming
The Prompts API documentation referenced the old type name `PromptProps`,
which has been renamed to `PromptsItemType` in the source. Update the
`items`, `onItemClick` and child item type references in both the
Chinese and English docs accordingly.

Closes #1863
* fix(x): refresh FileCard.List scroll ping state on items change

When Attachments uses overflow="scrollX" and items are updated from
outside (not via the internal upload flow), the scroll container's
clientWidth stays the same while only scrollWidth grows. The
ResizeObserver therefore does not fire and onScroll never happens, so
checkPing() is not called and the prev/next scroll buttons never show.

Add a useLayoutEffect that re-runs checkPing() via requestAnimationFrame
whenever the rendered list (or overflow mode) changes, covering the
controlled items-update case that ResizeObserver misses.

Closes #1911

* refactor(x): memoize checkPing with useCallback to satisfy exhaustive-deps

Address review feedback: wrap checkPing in React.useCallback (deps:
[overflow]) and add it to the useLayoutEffect dependency array so the
exhaustive-deps rule is satisfied.

* revert: drop unnecessary useCallback wrapping of checkPing

The project's biome config disables `useExhaustiveDependencies`
(see biome.json), so wrapping checkPing in useCallback to satisfy the
exhaustive-deps rule is unnecessary. Since checkPing only depends on
`overflow` (already in the effect deps), the memoization changes nothing
behaviorally. Revert to keep the code consistent with the file's style.
Custom tag inner content is treated as opaque text, but the protection
logic only escaped blank lines (`\n\n`). Single newlines followed by
block-level markdown (ordered/unordered lists, headings) were still
parsed by marked, splitting the custom tag structure (e.g. the closing
`</think>` was pulled into an `<li>`).

Now every newline inside a custom tag is protected. Placeholders use
Unicode Private Use Area sentinels instead of `__...__` so they are not
interpreted as markdown emphasis when adjacent to text.
Protecting every newline inside custom tags changed the behavior of the
existing `protectCustomTagNewlines` flag: block-level markdown (lists,
headings, quotes) that used to render inside custom tags became opaque
text, which is a breaking change for current users.

Restore `protectCustomTagNewlines` to its original semantics (only
protect blank-line paragraph breaks) and add a new opt-in flag
`protectAllCustomTagNewlines` that protects every newline. Both default
to false so existing behavior is unchanged.
The provider cache Map was created inside the component body, so it was
re-created on every render and never actually cached the provider. As a
result useXChat's requestHandlerRef pointed at a freshly-built provider
whose request had never run, making abort() throw and unable to cancel the
in-flight request when stopping a streaming response.

Persist the cache with useRef so the provider instance stays stable across
renders, aligning with the existing session-key demo. Fixes #1833.
…g-newline

fix(x-markdown): protect all newlines inside custom tags (#1896)
* feat: Added right-click functionality for the Folder component (#1919)

* feat: folder

* feat: folder feat

* feat: folder feat

* feat: folder feat

* feat: folder feat

* chore: update version

* feat: lingxi-dev

* chore: update version

* chore: update version

* feat: lingxi-dev

* feat: lingxi-dev

* chore: update version

* chore: update version

* fix: exclude .dumi directory from TypeScript type checking

* revert: restore .dumi tsconfig to original state

* chore: update version

* chore: update version

* fix: add dumi type declarations to fix lint errors

* feat: lint

* feat: lint

* feat: lint

* feat: lint

* chore: merge main to feature (#1923)

* fix: fix ci

* feat: folder

* feat: folder feat

* feat: folder feat

* feat: folder feat

* feat: folder feat

* feat(x-markdown): add disableDefaultStyles to opt out of default tag styles

Default tag styles are applied via descendant selectors on the `.x-markdown`
container (e.g. `.x-markdown ul`, `.x-markdown li`, `.x-markdown code`), so they
leak into elements rendered by custom components (such as antd `Collapse`) that
are mounted inside the container, polluting their styles.

Add a `disableDefaultStyles` prop:
- `true` disables all built-in tag styles
- an array (e.g. `['ul', 'ol', 'li']`) disables only the listed tags

Implemented purely at the view layer: the root container gets `x-md-disable-all`
or per-tag `x-md-disable-<tag>` classes, and each default rule in index.css is
gated behind `:not(.x-md-disable-all):not(.x-md-disable-<tag>)`. The descendant
selector strategy (so nested Markdown inherits defaults) is preserved; only the
opt-out is added. Parser/Renderer core is untouched.

closes #1908

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x): coerce string|number design tokens before arithmetic

CI's antd version types `fontSizeHeading1/2` tokens as `string | number`,
breaking `token.fontSizeHeading1 + 10` (TS2365) and `token.fontSizeHeading2 * 2`
(TS2362). Wrap with `Number()` so the arithmetic is valid regardless of the
token's declared type.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x-markdown): gate list markers by list container disable class

`list-style` on `ul > li` / `ol > li` was gated only by `:not(.x-md-disable-li)`,
so `disableDefaultStyles={['ul']}` left the bullets/numbers in place — users had
to also pass `'li'`. Also gate these selectors by the container's disable class
so disabling `ul`/`ol` removes their markers as expected, while keeping the `li`
clause (no regression) and ordered/unordered independence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: fix ci

* fix: fix ci

* fix: avoid jank when initial streaming output is non-streaming

Seed output with full input for non-streaming to prevent layout jitter.

Signed-off-by: Waiter <waitersong2012@gmail.com>

* Refactor streaming output handling in useStreaming hook

Signed-off-by: Waiter <waitersong2012@gmail.com>

* chore: update test snapshot

* test(x-markdown): fix nodeName getter for happy-dom regression

happy-dom (>= 20.10.x) returns '' from the Node.prototype.nodeName getter
for element instances. DOMPurify reads nodeName via Node.prototype, so every
element was treated as an unknown tag and stripped (keeping only text),
causing 24 Renderer streamStatus tests to fail. Restore a spec-correct
nodeName getter on Node.prototype in the jest setup. Test-env only;
real browsers and jsdom are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x-markdown): guard DOMPurify against SSR (no DOM)

The streaming refactor seeds non-streaming output synchronously on first
render, so XMarkdown now renders during SSR too. DOMPurify's default export
has no `sanitize` method without a `window`, so server pre-render crashed
with "sanitize is not a function". Return null from processHtml when there
is no DOM, letting the client hydrate — same as the pre-refactor server
behavior. No new deps, no XSS exposure (server emits nothing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(x-markdown): cover SSR no-DOM guard in Renderer.processHtml

Exercise the branch where DOMPurify.sanitize is unavailable (server pre-render)
by temporarily clearing it, asserting processHtml/render return null instead of
throwing. Raises patch coverage for the SSR guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: fix outdated type name in Prompts API table (#1915)

The Prompts API documentation referenced the old type name `PromptProps`,
which has been renamed to `PromptsItemType` in the source. Update the
`items`, `onItemClick` and child item type references in both the
Chinese and English docs accordingly.

Closes #1863

* fix(x): refresh FileCard.List scroll ping state on items change (#1914)

* fix(x): refresh FileCard.List scroll ping state on items change

When Attachments uses overflow="scrollX" and items are updated from
outside (not via the internal upload flow), the scroll container's
clientWidth stays the same while only scrollWidth grows. The
ResizeObserver therefore does not fire and onScroll never happens, so
checkPing() is not called and the prev/next scroll buttons never show.

Add a useLayoutEffect that re-runs checkPing() via requestAnimationFrame
whenever the rendered list (or overflow mode) changes, covering the
controlled items-update case that ResizeObserver misses.

Closes #1911

* refactor(x): memoize checkPing with useCallback to satisfy exhaustive-deps

Address review feedback: wrap checkPing in React.useCallback (deps:
[overflow]) and add it to the useLayoutEffect dependency array so the
exhaustive-deps rule is satisfied.

* revert: drop unnecessary useCallback wrapping of checkPing

The project's biome config disables `useExhaustiveDependencies`
(see biome.json), so wrapping checkPing in useCallback to satisfy the
exhaustive-deps rule is unnecessary. Since checkPing only depends on
`overflow` (already in the effect deps), the memoization changes nothing
behaviorally. Revert to keep the code consistent with the file's style.

* chore: update version

* feat: lingxi-dev

* chore: update version

* chore: update version

* feat: lingxi-dev

* feat: lingxi-dev

* chore: update version

* chore: update version

* fix: exclude .dumi directory from TypeScript type checking

* revert: restore .dumi tsconfig to original state

* chore: update version

* chore: update version

* fix: add dumi type declarations to fix lint errors

* feat: lint

* feat: lint

* feat: lint

* feat: lint

* fix: add missing DOMPurify import in Renderer.test.ts

* feat: lint

* ci: pinned dumi version to 2.4.28 (#1922)

* feat: lingxi-dev

---------

Signed-off-by: Waiter <waitersong2012@gmail.com>
Co-authored-by: Div627 <chenyang_nn@163.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Div627 <54497462+Div627@users.noreply.github.com>
Co-authored-by: Waiter <waitersong2012@gmail.com>
Co-authored-by: Ryker_Feng <90562015+18062706139fcz@users.noreply.github.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>

* chore: merge mian to feature (#1924)

* fix: fix ci

* feat: folder

* feat: folder feat

* feat: folder feat

* feat: folder feat

* feat: folder feat

* feat(x-markdown): add disableDefaultStyles to opt out of default tag styles

Default tag styles are applied via descendant selectors on the `.x-markdown`
container (e.g. `.x-markdown ul`, `.x-markdown li`, `.x-markdown code`), so they
leak into elements rendered by custom components (such as antd `Collapse`) that
are mounted inside the container, polluting their styles.

Add a `disableDefaultStyles` prop:
- `true` disables all built-in tag styles
- an array (e.g. `['ul', 'ol', 'li']`) disables only the listed tags

Implemented purely at the view layer: the root container gets `x-md-disable-all`
or per-tag `x-md-disable-<tag>` classes, and each default rule in index.css is
gated behind `:not(.x-md-disable-all):not(.x-md-disable-<tag>)`. The descendant
selector strategy (so nested Markdown inherits defaults) is preserved; only the
opt-out is added. Parser/Renderer core is untouched.

closes #1908

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x): coerce string|number design tokens before arithmetic

CI's antd version types `fontSizeHeading1/2` tokens as `string | number`,
breaking `token.fontSizeHeading1 + 10` (TS2365) and `token.fontSizeHeading2 * 2`
(TS2362). Wrap with `Number()` so the arithmetic is valid regardless of the
token's declared type.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x-markdown): gate list markers by list container disable class

`list-style` on `ul > li` / `ol > li` was gated only by `:not(.x-md-disable-li)`,
so `disableDefaultStyles={['ul']}` left the bullets/numbers in place — users had
to also pass `'li'`. Also gate these selectors by the container's disable class
so disabling `ul`/`ol` removes their markers as expected, while keeping the `li`
clause (no regression) and ordered/unordered independence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: fix ci

* fix: fix ci

* fix: avoid jank when initial streaming output is non-streaming

Seed output with full input for non-streaming to prevent layout jitter.

Signed-off-by: Waiter <waitersong2012@gmail.com>

* Refactor streaming output handling in useStreaming hook

Signed-off-by: Waiter <waitersong2012@gmail.com>

* chore: update test snapshot

* test(x-markdown): fix nodeName getter for happy-dom regression

happy-dom (>= 20.10.x) returns '' from the Node.prototype.nodeName getter
for element instances. DOMPurify reads nodeName via Node.prototype, so every
element was treated as an unknown tag and stripped (keeping only text),
causing 24 Renderer streamStatus tests to fail. Restore a spec-correct
nodeName getter on Node.prototype in the jest setup. Test-env only;
real browsers and jsdom are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x-markdown): guard DOMPurify against SSR (no DOM)

The streaming refactor seeds non-streaming output synchronously on first
render, so XMarkdown now renders during SSR too. DOMPurify's default export
has no `sanitize` method without a `window`, so server pre-render crashed
with "sanitize is not a function". Return null from processHtml when there
is no DOM, letting the client hydrate — same as the pre-refactor server
behavior. No new deps, no XSS exposure (server emits nothing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(x-markdown): cover SSR no-DOM guard in Renderer.processHtml

Exercise the branch where DOMPurify.sanitize is unavailable (server pre-render)
by temporarily clearing it, asserting processHtml/render return null instead of
throwing. Raises patch coverage for the SSR guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: fix outdated type name in Prompts API table (#1915)

The Prompts API documentation referenced the old type name `PromptProps`,
which has been renamed to `PromptsItemType` in the source. Update the
`items`, `onItemClick` and child item type references in both the
Chinese and English docs accordingly.

Closes #1863

* fix(x): refresh FileCard.List scroll ping state on items change (#1914)

* fix(x): refresh FileCard.List scroll ping state on items change

When Attachments uses overflow="scrollX" and items are updated from
outside (not via the internal upload flow), the scroll container's
clientWidth stays the same while only scrollWidth grows. The
ResizeObserver therefore does not fire and onScroll never happens, so
checkPing() is not called and the prev/next scroll buttons never show.

Add a useLayoutEffect that re-runs checkPing() via requestAnimationFrame
whenever the rendered list (or overflow mode) changes, covering the
controlled items-update case that ResizeObserver misses.

Closes #1911

* refactor(x): memoize checkPing with useCallback to satisfy exhaustive-deps

Address review feedback: wrap checkPing in React.useCallback (deps:
[overflow]) and add it to the useLayoutEffect dependency array so the
exhaustive-deps rule is satisfied.

* revert: drop unnecessary useCallback wrapping of checkPing

The project's biome config disables `useExhaustiveDependencies`
(see biome.json), so wrapping checkPing in useCallback to satisfy the
exhaustive-deps rule is unnecessary. Since checkPing only depends on
`overflow` (already in the effect deps), the memoization changes nothing
behaviorally. Revert to keep the code consistent with the file's style.

* chore: update version

* feat: lingxi-dev

* chore: update version

* chore: update version

* feat: lingxi-dev

* feat: lingxi-dev

* chore: update version

* chore: update version

* fix: exclude .dumi directory from TypeScript type checking

* revert: restore .dumi tsconfig to original state

* chore: update version

* chore: update version

* fix: add dumi type declarations to fix lint errors

* feat: lint

* feat: lint

* feat: lint

* feat: lint

* fix: add missing DOMPurify import in Renderer.test.ts

* feat: lint

* ci: pinned dumi version to 2.4.28 (#1922)

* feat: lingxi-dev

* chore: merge

---------

Signed-off-by: Waiter <waitersong2012@gmail.com>
Co-authored-by: Div627 <chenyang_nn@163.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Div627 <54497462+Div627@users.noreply.github.com>
Co-authored-by: Waiter <waitersong2012@gmail.com>
Co-authored-by: Ryker_Feng <90562015+18062706139fcz@users.noreply.github.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>

* feat: merge

* feat: lingxi-dev

* feat: lingxi-dev

* chore: feature merge main (#1927)

* fix: fix ci

* feat(x-markdown): add disableDefaultStyles to opt out of default tag styles

Default tag styles are applied via descendant selectors on the `.x-markdown`
container (e.g. `.x-markdown ul`, `.x-markdown li`, `.x-markdown code`), so they
leak into elements rendered by custom components (such as antd `Collapse`) that
are mounted inside the container, polluting their styles.

Add a `disableDefaultStyles` prop:
- `true` disables all built-in tag styles
- an array (e.g. `['ul', 'ol', 'li']`) disables only the listed tags

Implemented purely at the view layer: the root container gets `x-md-disable-all`
or per-tag `x-md-disable-<tag>` classes, and each default rule in index.css is
gated behind `:not(.x-md-disable-all):not(.x-md-disable-<tag>)`. The descendant
selector strategy (so nested Markdown inherits defaults) is preserved; only the
opt-out is added. Parser/Renderer core is untouched.

closes #1908

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x): coerce string|number design tokens before arithmetic

CI's antd version types `fontSizeHeading1/2` tokens as `string | number`,
breaking `token.fontSizeHeading1 + 10` (TS2365) and `token.fontSizeHeading2 * 2`
(TS2362). Wrap with `Number()` so the arithmetic is valid regardless of the
token's declared type.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x-markdown): gate list markers by list container disable class

`list-style` on `ul > li` / `ol > li` was gated only by `:not(.x-md-disable-li)`,
so `disableDefaultStyles={['ul']}` left the bullets/numbers in place — users had
to also pass `'li'`. Also gate these selectors by the container's disable class
so disabling `ul`/`ol` removes their markers as expected, while keeping the `li`
clause (no regression) and ordered/unordered independence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: fix ci

* fix: fix ci

* fix: avoid jank when initial streaming output is non-streaming

Seed output with full input for non-streaming to prevent layout jitter.

Signed-off-by: Waiter <waitersong2012@gmail.com>

* Refactor streaming output handling in useStreaming hook

Signed-off-by: Waiter <waitersong2012@gmail.com>

* chore: update test snapshot

* test(x-markdown): fix nodeName getter for happy-dom regression

happy-dom (>= 20.10.x) returns '' from the Node.prototype.nodeName getter
for element instances. DOMPurify reads nodeName via Node.prototype, so every
element was treated as an unknown tag and stripped (keeping only text),
causing 24 Renderer streamStatus tests to fail. Restore a spec-correct
nodeName getter on Node.prototype in the jest setup. Test-env only;
real browsers and jsdom are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x-markdown): guard DOMPurify against SSR (no DOM)

The streaming refactor seeds non-streaming output synchronously on first
render, so XMarkdown now renders during SSR too. DOMPurify's default export
has no `sanitize` method without a `window`, so server pre-render crashed
with "sanitize is not a function". Return null from processHtml when there
is no DOM, letting the client hydrate — same as the pre-refactor server
behavior. No new deps, no XSS exposure (server emits nothing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(x-markdown): cover SSR no-DOM guard in Renderer.processHtml

Exercise the branch where DOMPurify.sanitize is unavailable (server pre-render)
by temporarily clearing it, asserting processHtml/render return null instead of
throwing. Raises patch coverage for the SSR guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: fix outdated type name in Prompts API table (#1915)

The Prompts API documentation referenced the old type name `PromptProps`,
which has been renamed to `PromptsItemType` in the source. Update the
`items`, `onItemClick` and child item type references in both the
Chinese and English docs accordingly.

Closes #1863

* fix(x): refresh FileCard.List scroll ping state on items change (#1914)

* fix(x): refresh FileCard.List scroll ping state on items change

When Attachments uses overflow="scrollX" and items are updated from
outside (not via the internal upload flow), the scroll container's
clientWidth stays the same while only scrollWidth grows. The
ResizeObserver therefore does not fire and onScroll never happens, so
checkPing() is not called and the prev/next scroll buttons never show.

Add a useLayoutEffect that re-runs checkPing() via requestAnimationFrame
whenever the rendered list (or overflow mode) changes, covering the
controlled items-update case that ResizeObserver misses.

Closes #1911

* refactor(x): memoize checkPing with useCallback to satisfy exhaustive-deps

Address review feedback: wrap checkPing in React.useCallback (deps:
[overflow]) and add it to the useLayoutEffect dependency array so the
exhaustive-deps rule is satisfied.

* revert: drop unnecessary useCallback wrapping of checkPing

The project's biome config disables `useExhaustiveDependencies`
(see biome.json), so wrapping checkPing in useCallback to satisfy the
exhaustive-deps rule is unnecessary. Since checkPing only depends on
`overflow` (already in the effect deps), the memoization changes nothing
behaviorally. Revert to keep the code consistent with the file's style.

* ci: pinned dumi version to 2.4.28 (#1922)

* docs: cache chat provider with useRef in x-conversations demos (#1925)

The provider cache Map was created inside the component body, so it was
re-created on every render and never actually cached the provider. As a
result useXChat's requestHandlerRef pointed at a freshly-built provider
whose request had never run, making abort() throw and unable to cancel the
in-flight request when stopping a streaming response.

Persist the cache with useRef so the provider instance stays stable across
renders, aligning with the existing session-key demo. Fixes #1833.

* feat: merge

* feat: lingxi-dev

* feat: lingxi-dev

---------

Signed-off-by: Waiter <waitersong2012@gmail.com>
Co-authored-by: Div627 <chenyang_nn@163.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Div627 <54497462+Div627@users.noreply.github.com>
Co-authored-by: Waiter <waitersong2012@gmail.com>
Co-authored-by: Ryker_Feng <90562015+18062706139fcz@users.noreply.github.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>

* chore: feature merge main (#1928)

* fix: fix ci

* feat(x-markdown): add disableDefaultStyles to opt out of default tag styles

Default tag styles are applied via descendant selectors on the `.x-markdown`
container (e.g. `.x-markdown ul`, `.x-markdown li`, `.x-markdown code`), so they
leak into elements rendered by custom components (such as antd `Collapse`) that
are mounted inside the container, polluting their styles.

Add a `disableDefaultStyles` prop:
- `true` disables all built-in tag styles
- an array (e.g. `['ul', 'ol', 'li']`) disables only the listed tags

Implemented purely at the view layer: the root container gets `x-md-disable-all`
or per-tag `x-md-disable-<tag>` classes, and each default rule in index.css is
gated behind `:not(.x-md-disable-all):not(.x-md-disable-<tag>)`. The descendant
selector strategy (so nested Markdown inherits defaults) is preserved; only the
opt-out is added. Parser/Renderer core is untouched.

closes #1908

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x): coerce string|number design tokens before arithmetic

CI's antd version types `fontSizeHeading1/2` tokens as `string | number`,
breaking `token.fontSizeHeading1 + 10` (TS2365) and `token.fontSizeHeading2 * 2`
(TS2362). Wrap with `Number()` so the arithmetic is valid regardless of the
token's declared type.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x-markdown): gate list markers by list container disable class

`list-style` on `ul > li` / `ol > li` was gated only by `:not(.x-md-disable-li)`,
so `disableDefaultStyles={['ul']}` left the bullets/numbers in place — users had
to also pass `'li'`. Also gate these selectors by the container's disable class
so disabling `ul`/`ol` removes their markers as expected, while keeping the `li`
clause (no regression) and ordered/unordered independence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: fix ci

* fix: fix ci

* fix: avoid jank when initial streaming output is non-streaming

Seed output with full input for non-streaming to prevent layout jitter.

Signed-off-by: Waiter <waitersong2012@gmail.com>

* Refactor streaming output handling in useStreaming hook

Signed-off-by: Waiter <waitersong2012@gmail.com>

* chore: update test snapshot

* test(x-markdown): fix nodeName getter for happy-dom regression

happy-dom (>= 20.10.x) returns '' from the Node.prototype.nodeName getter
for element instances. DOMPurify reads nodeName via Node.prototype, so every
element was treated as an unknown tag and stripped (keeping only text),
causing 24 Renderer streamStatus tests to fail. Restore a spec-correct
nodeName getter on Node.prototype in the jest setup. Test-env only;
real browsers and jsdom are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(x-markdown): guard DOMPurify against SSR (no DOM)

The streaming refactor seeds non-streaming output synchronously on first
render, so XMarkdown now renders during SSR too. DOMPurify's default export
has no `sanitize` method without a `window`, so server pre-render crashed
with "sanitize is not a function". Return null from processHtml when there
is no DOM, letting the client hydrate — same as the pre-refactor server
behavior. No new deps, no XSS exposure (server emits nothing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(x-markdown): cover SSR no-DOM guard in Renderer.processHtml

Exercise the branch where DOMPurify.sanitize is unavailable (server pre-render)
by temporarily clearing it, asserting processHtml/render return null instead of
throwing. Raises patch coverage for the SSR guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: fix outdated type name in Prompts API table (#1915)

The Prompts API documentation referenced the old type name `PromptProps`,
which has been renamed to `PromptsItemType` in the source. Update the
`items`, `onItemClick` and child item type references in both the
Chinese and English docs accordingly.

Closes #1863

* fix(x): refresh FileCard.List scroll ping state on items change (#1914)

* fix(x): refresh FileCard.List scroll ping state on items change

When Attachments uses overflow="scrollX" and items are updated from
outside (not via the internal upload flow), the scroll container's
clientWidth stays the same while only scrollWidth grows. The
ResizeObserver therefore does not fire and onScroll never happens, so
checkPing() is not called and the prev/next scroll buttons never show.

Add a useLayoutEffect that re-runs checkPing() via requestAnimationFrame
whenever the rendered list (or overflow mode) changes, covering the
controlled items-update case that ResizeObserver misses.

Closes #1911

* refactor(x): memoize checkPing with useCallback to satisfy exhaustive-deps

Address review feedback: wrap checkPing in React.useCallback (deps:
[overflow]) and add it to the useLayoutEffect dependency array so the
exhaustive-deps rule is satisfied.

* revert: drop unnecessary useCallback wrapping of checkPing

The project's biome config disables `useExhaustiveDependencies`
(see biome.json), so wrapping checkPing in useCallback to satisfy the
exhaustive-deps rule is unnecessary. Since checkPing only depends on
`overflow` (already in the effect deps), the memoization changes nothing
behaviorally. Revert to keep the code consistent with the file's style.

* fix(x-markdown): protect all newlines inside custom tags (#1896)

Custom tag inner content is treated as opaque text, but the protection
logic only escaped blank lines (`\n\n`). Single newlines followed by
block-level markdown (ordered/unordered lists, headings) were still
parsed by marked, splitting the custom tag structure (e.g. the closing
`</think>` was pulled into an `<li>`).

Now every newline inside a custom tag is protected. Placeholders use
Unicode Private Use Area sentinels instead of `__...__` so they are not
interpreted as markdown emphasis when adjacent to text.

* fix(x-markdown): split full-newline protection into a separate flag

Protecting every newline inside custom tags changed the behavior of the
existing `protectCustomTagNewlines` flag: block-level markdown (lists,
headings, quotes) that used to render inside custom tags became opaque
text, which is a breaking change for current users.

Restore `protectCustomTagNewlines` to its original semantics (only
protect blank-line paragraph breaks) and add a new opt-in flag
`protectAllCustomTagNewlines` that protects every newline. Both default
to false so existing behavior is unchanged.

* ci: pinned dumi version to 2.4.28 (#1922)

* refactor(x-markdown): clarify custom tag block markdown option

* refactor(x-markdown): align internal flag name with public option

* docs: cache chat provider with useRef in x-conversations demos (#1925)

The provider cache Map was created inside the component body, so it was
re-created on every render and never actually cached the provider. As a
result useXChat's requestHandlerRef pointed at a freshly-built provider
whose request had never run, making abort() throw and unable to cancel the
in-flight request when stopping a streaming response.

Persist the cache with useRef so the provider instance stays stable across
renders, aligning with the existing session-key demo. Fixes #1833.

* feat: merge

* feat: lingxi-dev

* feat: lingxi-dev

---------

Signed-off-by: Waiter <waitersong2012@gmail.com>
Co-authored-by: Div627 <chenyang_nn@163.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Div627 <54497462+Div627@users.noreply.github.com>
Co-authored-by: Waiter <waitersong2012@gmail.com>
Co-authored-by: Ryker_Feng <90562015+18062706139fcz@users.noreply.github.com>
Co-authored-by: ryker <2279549769@qq.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>

---------

Signed-off-by: Waiter <waitersong2012@gmail.com>
Co-authored-by: Div627 <chenyang_nn@163.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Div627 <54497462+Div627@users.noreply.github.com>
Co-authored-by: Waiter <waitersong2012@gmail.com>
Co-authored-by: Ryker_Feng <90562015+18062706139fcz@users.noreply.github.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
Co-authored-by: ryker <2279549769@qq.com>
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 70879a29-f91a-481a-a6ef-17c0708681c5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

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.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces several enhancements and fixes across the repository, notably adding a contextMenu feature and ref methods (getNode, updateNode, deleteNode, addNode) to the Folder component, implementing a disableCustomTagBlockMarkdown option and a disableDefaultStyles prop in XMarkdown, and patching DOMPurify to resolve a compatibility issue with happy-dom. Feedback on the changes suggests refactoring the Folder component's DirectoryTree to use useMemo instead of writing to a useRef during render to avoid React anti-patterns and potential memory leaks. Additionally, it is recommended to lazily initialize providerCachesRef in the conversations demo to prevent unnecessary Map instantiations on every render.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

const [contextMenuItems, setContextMenuItems] = useState<MenuProps['items']>(undefined);

// Store all original node data indexed by key for quick lookup
const nodeDataMapRef = useRef<Map<string, FolderTreeData>>(new Map());

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.

medium

Writing to a useRef during render is a React anti-pattern and can lead to unpredictable behavior in concurrent rendering or strict mode. Additionally, since the map is never cleared, it can cause a memory leak when nodes are dynamically updated or deleted. Consider using useMemo to build the node data map from treeData instead.

  // Store all original node data indexed by key for quick lookup
  const nodeDataMap = React.useMemo(() => {
    const map = new Map<string, FolderTreeData>();
    const traverse = (nodes?: FolderTreeData[], parentSegments: string[] = []) => {
      nodes?.forEach((node) => {
        const pathSegments = [...parentSegments, node.path];
        const fullPath = pathSegments.join('/').replace(/^\/+/, '');
        map.set(fullPath, node);
        if (node.children) {
          traverse(node.children, pathSegments);
        }
      });
    };
    traverse(treeData);
    return map;
  }, [treeData]);

const handleRightClick: TreeProps['onRightClick'] = (info) => {
const { node } = info;
const nodeKey = node.key as string;
const originalNode = nodeDataMapRef.current.get(nodeKey);

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.

medium

Update the right-click handler to read from the memoized nodeDataMap instead of the ref.

Suggested change
const originalNode = nodeDataMapRef.current.get(nodeKey);
const originalNode = nodeDataMap.get(nodeKey);

// 提供者缓存:为每个会话缓存独立的聊天提供者实例
// Provider cache: cache independent chat provider instances for each conversation
const providerCaches = new Map<string, DeepSeekChatProvider>();
const providerCachesRef = useRef(new Map<string, DeepSeekChatProvider>());

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.

medium

To avoid instantiating a new Map() on every single render, use lazy initialization for the ref. This also aligns with the implementation pattern used in the other demo files (e.g., with-x-chat.tsx).

Suggested change
const providerCachesRef = useRef(new Map<string, DeepSeekChatProvider>());
const providerCachesRef = useRef<Map<string, DeepSeekChatProvider> | null>(null);

// 提供者工厂:根据会话key创建或获取对应的聊天提供者
// Provider factory: create or get corresponding chat provider based on conversation key
const providerFactory = (conversationKey: string) => {
const providerCaches = providerCachesRef.current;

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.

medium

Lazily initialize the provider cache map if it hasn't been created yet.

    if (!providerCachesRef.current) {
      providerCachesRef.current = new Map();
    }
    const providerCaches = providerCachesRef.current;

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Performance benchmark report not found.


This comment is automatically generated by the x-markdown performance CI.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying ant-design-x with  Cloudflare Pages  Cloudflare Pages

Latest commit: a270320
Status: ✅  Deploy successful!
Preview URL: https://2af03ae3.ant-design-x.pages.dev

View logs

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size
packages/x/dist/antdx.min.js 510.62 KB
packages/x-sdk/dist/x-sdk.min.js 55.66 KB
packages/x-markdown/dist/x-markdown.min.js 33.8 KB
packages/x-markdown/dist/plugins/latex.min.js 61.91 KB

@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.62162% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.09%. Comparing base (44ad530) to head (a270320).
⚠️ Report is 7 commits behind head on feature.

Files with missing lines Patch % Lines
packages/x-markdown/src/XMarkdown/core/Renderer.ts 53.22% 29 Missing ⚠️
packages/x/components/folder/DirectoryTree.tsx 92.50% 3 Missing ⚠️
packages/x/components/folder/index.tsx 95.23% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           feature    #1930      +/-   ##
===========================================
- Coverage    97.62%   97.09%   -0.53%     
===========================================
  Files          158      158              
  Lines         5547     5711     +164     
  Branches      1621     1676      +55     
===========================================
+ Hits          5415     5545     +130     
- Misses         130      164      +34     
  Partials         2        2              

☔ View full report in Codecov by Harness.
📢 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.

@kimteayon kimteayon closed this Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants