Skip to content

chore: feature merge main#1928

Merged
kimteayon merged 33 commits into
featurefrom
feature-merge-main
Jun 8, 2026
Merged

chore: feature merge main#1928
kimteayon merged 33 commits into
featurefrom
feature-merge-main

Conversation

@kimteayon

Copy link
Copy Markdown
Collaborator

中文版模板 / Chinese template

🤔 This is a ...

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

🔗 Related Issues

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

💡 Background and Solution

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

📝 Change Log

Language Changelog
🇺🇸 English
🇨🇳 Chinese

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

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

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

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

closes #1908

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

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

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

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

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

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

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

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

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

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

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

Closes #1911

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

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

* revert: drop unnecessary useCallback wrapping of checkPing

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

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

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

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

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

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

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

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

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 841bf46a-d601-4faa-8f9d-bfbad47e5d4e

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 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

Bundle size has no change ✅

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new option disableCustomTagBlockMarkdown to disable block-level Markdown parsing inside custom tags while keeping inline Markdown parsing intact. It also refactors the newline placeholder system to use Unicode Private Use Area characters to prevent them from being interpreted as Markdown syntax. The reviewer suggests optimizing the blank-line preservation logic by mapping consecutive newlines to a single placeholder instead of executing nested regex replacements for each individual newline character.

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 +275 to +277
const protectedInner = innerContent.replace(/\n{2,}/g, (newlines) =>
protectNewlines(newlines),
);

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 protecting blank-line paragraph breaks (\n\n or more), calling protectNewlines(newlines) performs a nested regex replacement for each individual newline character and creates multiple placeholders in the map.

Instead, we can map the entire sequence of newlines to a single placeholder. This avoids the nested regex replacement and reduces the overhead of the placeholders map, improving both performance and code simplicity.

Suggested change
const protectedInner = innerContent.replace(/\n{2,}/g, (newlines) =>
protectNewlines(newlines),
);
const protectedInner = innerContent.replace(/\n{2,}/g, (newlines) => {
const ph = `${PLACEHOLDER_PREFIX}${placeholderIndex++}${PLACEHOLDER_SUFFIX}`;
placeholders.set(ph, newlines);
return ph;
});

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Performance benchmark report not found.


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

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying ant-design-x with  Cloudflare Pages  Cloudflare Pages

Latest commit: cbfbb9b
Status: ✅  Deploy successful!
Preview URL: https://f1c3fea4.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

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.09%. Comparing base (4e1e0c8) to head (cbfbb9b).
⚠️ Report is 1 commits behind head on feature.

Additional details and impacted files
@@           Coverage Diff            @@
##           feature    #1928   +/-   ##
========================================
  Coverage    97.08%   97.09%           
========================================
  Files          158      158           
  Lines         5703     5711    +8     
  Branches      1694     1665   -29     
========================================
+ Hits          5537     5545    +8     
  Misses         164      164           
  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
packages/x-sdk/dist/x-sdk.min.js 55.66 KB
packages/x-markdown/dist/x-markdown.min.js 33.8 KB (+120 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 c3dbf32 into feature Jun 8, 2026
15 checks passed
@kimteayon kimteayon deleted the feature-merge-main branch June 8, 2026 08:29
@kimteayon kimteayon restored the feature-merge-main branch June 8, 2026 08:31
kimteayon added a commit that referenced this pull request Jun 8, 2026
* 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>
kimteayon added a commit that referenced this pull request Jun 8, 2026
* 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>
kimteayon added a commit that referenced this pull request Jun 8, 2026
* 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>

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

5 participants