Skip to content

chore: main merge feature#1929

Merged
kimteayon merged 12 commits into
mainfrom
feature-merge-main
Jun 8, 2026
Merged

chore: main merge feature#1929
kimteayon merged 12 commits into
mainfrom
feature-merge-main

Conversation

@kimteayon

@kimteayon kimteayon commented Jun 8, 2026

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

Summary by CodeRabbit

  • New Features

    • Folder组件新增右键上下文菜单,支持节点级与全局配置
    • Folder组件新增命令式操作API(getNode、updateNode、deleteNode、addNode)
    • Markdown渲染器修复DOMPurify兼容性问题
  • Tests

    • 扩展Folder组件与XMarkdown的测试覆盖
  • Chores

    • 升级版本至2.8.0

kimteayon and others added 12 commits June 8, 2026 11:20
* 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
* 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>
* 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>
* 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>
* 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>
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

总体概览

本 PR 发布 v2.8.0 版本,跨多个工作区包更新版本号,并在 Folder 组件中新增右键菜单与命令式树操作能力;同时修复 x-markdown 中 DOMPurify 在特定环境的 Node.prototype getter 缓存异常,进行多处代码表达式优化。

改动说明

版本与配置更新

层级 / 文件 摘要
版本号与配置调整
package.jsonpackages/x*/package.jsonpackages/x-skill/skills*/SKILL.mdpackages/x-skill/src/skill-meta.jsonbiome.jsontsconfig.json.gitignore
根项目及所有工作区包(x-cardx-markdownx-sdkx-skillx)的版本从 2.7.0 更新至 2.8.0;skill-meta.json 中部分技能版本更新至 2.9.0;调整 TypeScript 编译范围与 biome 代码检查排除模式。

Folder 组件右键菜单功能

层级 / 文件 摘要
菜单渲染与交互逻辑
packages/x/components/folder/DirectoryTree.tsx
新增 contextMenuonRightClick 属性支持,在 DirectoryTree 中引入 Dropdown 实现右键菜单触发;通过 nodeDataMapRef 缓存节点数据,按节点级 → 全局级优先级匹配菜单项;使用 isRightClickRef 拦截右键后的 onSelect 触发以避免误选。
Folder 命令式 API 与类型导出
packages/x/components/folder/index.tsxpackages/x/components/index.ts
扩展 FolderRef 类型,新增 getNodeupdateNodedeleteNodeaddNode 四个不可变树操作方法;新增 walkTree 递归遍历函数;重导出 FolderTreeData 类型供外部使用。
API 文档与演示
packages/x/components/folder/index.en-US.mdpackages/x/components/folder/index.zh-CN.mdpackages/x/components/folder/demo/context-menu.mdpackages/x/components/folder/demo/context-menu.tsx
补充 contextMenuonRightClick 的 API 文档,新增 Ref Methods 章节说明四个命令式方法;新增完整演示组件展示节点级菜单覆盖、编辑/删除/新增/复制等交互。
单元测试与分支覆盖
packages/x/components/folder/__tests__/index.test.tsx
新增 contextMenu 测试分组验证全局/节点级菜单、菜单禁用、优先级;新增 FolderRef methods 测试验证四个命令式方法的查找、更新、删除、添加及不可变性;补充 branch coveragetargeted branch coverage 测试覆盖多条实现分支。

x-markdown DOMPurify 修补与优化

层级 / 文件 摘要
DOMPurify Node.prototype 修补逻辑
packages/x-markdown/src/XMarkdown/core/Renderer.ts
新增 createPatchedDOMPurify 函数检测 Node.prototype 的 nodeName/nodeValue getter 在模板内容场景下是否失效;在失效时通过 Object.defineProperty 注入回退逻辑,创建新的 DOMPurify 实例;processHtml 改用补丁实例执行 sanitize。
测试策略调整
packages/x-markdown/src/XMarkdown/__tests__/Renderer.test.ts
将 DOMPurify 测试从直接 spy sanitize 调用改为监控 React.createElement,验证渲染结果;新增 createPatchedDOMPurify 分组测试补丁方案在不同节点类型、属性白名单、HTML 注释、streaming 动画等场景的表现。
代码简化
packages/x-markdown/src/XMarkdown/core/Parser.tspackages/x-markdown/src/XMarkdown/hooks/useStreaming.ts
使用可选链 ?. 简化 findLastTextToken 的空值判定;移除 useStreamingoutput 赋值表达式的多余括号。

其他代码优化

层级 / 文件 摘要
可选链与延迟初始化优化
packages/x/components/attachments/util.tspackages/x/docs/x-markdown/demo/streaming/format.tsxpackages/x/docs/x-sdk/demos/chat-providers/custom-provider-width-ui.tsxpackages/x/docs/x-sdk/demos/x-conversations/with-x-chat.tsx
previewImageIncompleteEmphasis 用可选链替代显式空值检查;transformMessage 简化流式完成检测;providerCachesRef 改为延迟初始化(null 而非 new Map())。
TypeScript 类型声明
packages/x/typings/custom-typings.d.ts
补充 Dumi 相关模块的 ambient type declaration,包含 dumi/theme-default/slots/SearchBardumi/dist/client/theme-api、顶层 dumi 模块的导出类型。

估算评审工作量

🎯 4 (Complex) | ⏱️ ~60 minutes

  • 复杂性因素:Folder 菜单功能涉及 DirectoryTree、Folder、ref API、演示代码、文档和多组测试的协调;DOMPurify 修补涉及浏览器环境检测、原型污染防御、缓存管理等细节逻辑;跨包版本更新虽然重复但需验证一致性。
  • 异质性:改动涵盖新特性开发、bug 修复、代码优化、文档更新、测试扩展,各自需要独立的理由检证。
  • 文件分散:超过 40 个文件的改动,其中 Folder 功能集中在 5-6 个核心文件,其余为版本号和配置微调。

相关 PR

  • ant-design/x#1917:同样修改 packages/x-markdown/src/XMarkdown/core/Renderer.tsprocessHtml DOMPurify 处理路径(本 PR 新增补丁型 DOMPurify 实例处理;关联 PR 在 DOM 不可用时添加守卫)。
  • ant-design/x#1925:在 packages/x/docs/x-sdk/demos/x-conversations/with-x-chat.tsx 中同样将缓存 Map 改为延迟初始化,providerCachesRefproviderFactory 修改逻辑一致。

建议标签

lgtm

建议评审者

  • Div627

🐰 一只兔子在版本号间跳跃,
菜单在右键处绽放,
DOMPurify 的 getter 被温柔修补,
可选链舞动,代码更清爽,
v2.8.0 的故事,就这样圆满~

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive PR 标题 'chore: main merge feature' 过于宽泛和模糊,没有明确描述主要变更内容。 建议使用更具体的标题来描述此次合并的主要功能或改动,如 'chore: merge feature branch with x-markdown and Folder improvements' 等。
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature-merge-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.

@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 1.87MB (53.78%) ⬆️⚠️, exceeding the configured threshold of 5%.

Bundle name Size Change
antdx-array-push 3.98MB 1.87MB (88.63%) ⬆️⚠️

Affected Assets, Files, and Routes:

view changes for bundle: antdx-array-push

Assets Changed:

Asset Name Size Change Total Size Change (%)
antdx.js (New) 3.98MB 3.98MB 100.0% 🚀
antdx.min.js (Deleted) -2.11MB 0 bytes -100.0% 🗑️

@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 bumps package versions to 2.8.0, introduces a DOMPurify 3.x compatibility patch for happy-dom environments, and adds right-click context menu support along with new imperative ref methods (getNode, updateNode, deleteNode, addNode) to the Folder component. The review feedback highlights several key improvements: decoupling handleSelect from isRightClickRef using native event properties to prevent click-swallowing, avoiding side effects in the render phase by removing nodeDataMapRef and casting info.node directly, and handling empty parentPath arrays to allow adding nodes to the root level.

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.

Comment on lines +191 to +198
// Intercept onSelect to skip selection triggered by right-click
const handleSelect: TreeProps['onSelect'] = (keys, info) => {
if (isRightClickRef.current) {
isRightClickRef.current = false;
return;
}
onSelect?.(keys, info);
};

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.

high

Decouple handleSelect from isRightClickRef by checking the native event's button type directly. Using isRightClickRef inside handleSelect can cause a click-swallowing bug where the first left-click to close an open context menu is ignored/swallowed because isRightClickRef is still true. Additionally, checking the native event ensures consistent selection behavior regardless of whether the node has a context menu.

Suggested change
// Intercept onSelect to skip selection triggered by right-click
const handleSelect: TreeProps['onSelect'] = (keys, info) => {
if (isRightClickRef.current) {
isRightClickRef.current = false;
return;
}
onSelect?.(keys, info);
};
// Intercept onSelect to skip selection triggered by right-click
const handleSelect: TreeProps['onSelect'] = (keys, info) => {
const nativeEvent = info.nativeEvent;
if (nativeEvent && (nativeEvent.button === 2 || nativeEvent.type === 'contextmenu')) {
return;
}
onSelect?.(keys, info);
};

Comment on lines +153 to +156
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

Writing to nodeDataMapRef.current during the render phase violates React's rules of render (render functions must be pure and free of side effects). Since rc-tree preserves all custom properties passed to treeData nodes, the original node properties (like contextMenu) are already directly accessible on info.node in the onRightClick callback. You can safely remove nodeDataMapRef entirely and cast info.node to FolderTreeData.

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

Comment on lines +215 to +216
addNode: (parentPath: string[], node: FolderTreeData) =>
treeData ? walkTree(treeData, parentPath, 0, 'add', node) : [],

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

When parentPath is empty ([]), walkTree will fail to match any segment and return the original tree unchanged, making it impossible to add a new node to the root of the tree. Handle parentPath.length === 0 as a special case to allow appending nodes to the root level.

    addNode: (parentPath: string[], node: FolderTreeData) => {
      if (!treeData) return [];
      if (parentPath.length === 0) {
        return [...treeData, node];
      }
      return walkTree(treeData, parentPath, 0, 'add', node);
    },

@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

cloudflare-workers-and-pages Bot commented Jun 8, 2026

Copy link
Copy Markdown

Deploying ant-design-x with  Cloudflare Pages  Cloudflare Pages

Latest commit: c823b6d
Status: ✅  Deploy successful!
Preview URL: https://424c9a6b.ant-design-x.pages.dev
Branch Preview URL: https://feature-merge-main.ant-design-x.pages.dev

View logs

@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.71233% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.09%. Comparing base (d212e12) to head (c823b6d).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
packages/x-markdown/src/XMarkdown/core/Renderer.ts 51.66% 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             @@
##             main    #1929      +/-   ##
==========================================
- Coverage   97.63%   97.09%   -0.54%     
==========================================
  Files         158      158              
  Lines        5572     5711     +139     
  Branches     1599     1692      +93     
==========================================
+ Hits         5440     5545     +105     
- 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.

@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 (+497 B 🔺)
packages/x-sdk/dist/x-sdk.min.js 55.66 KB
packages/x-markdown/dist/x-markdown.min.js 33.8 KB (+350 B 🔺)
packages/x-markdown/dist/plugins/latex.min.js 61.91 KB

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jun 8, 2026
@kimteayon kimteayon merged commit a270320 into main Jun 8, 2026
13 of 16 checks passed
@kimteayon kimteayon deleted the feature-merge-main branch June 8, 2026 08:42
@kimteayon kimteayon restored the feature-merge-main branch June 8, 2026 08:57
kimteayon added a commit that referenced this pull request Jun 8, 2026
* 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.

* chore: main merge feature (#1929)

* 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>

* chore: main feature mian (#1932)

* 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>

* chore: Fix/sync feature with main (#1933)

* 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…
@coderabbitai coderabbitai Bot mentioned this pull request Jun 30, 2026
17 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BranchAutoMerge lgtm This PR has been approved by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants