Conversation
…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>
…sable-default-styles
…sable-default-styles
…ult-styles feat(x-markdown): add disableDefaultStyles to opt out of default tag styles
Seed output with full input for non-streaming to prevent layout jitter. Signed-off-by: Waiter <waitersong2012@gmail.com>
Signed-off-by: Waiter <waitersong2012@gmail.com>
happy-dom (>= 20.10.x) returns '' from the Node.prototype.nodeName getter for element instances. DOMPurify reads nodeName via Node.prototype, so every element was treated as an unknown tag and stripped (keeping only text), causing 24 Renderer streamStatus tests to fail. Restore a spec-correct nodeName getter on Node.prototype in the jest setup. Test-env only; real browsers and jsdom are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The streaming refactor seeds non-streaming output synchronously on first render, so XMarkdown now renders during SSR too. DOMPurify's default export has no `sanitize` method without a `window`, so server pre-render crashed with "sanitize is not a function". Return null from processHtml when there is no DOM, letting the client hydrate — same as the pre-refactor server behavior. No new deps, no XSS exposure (server emits nothing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Exercise the branch where DOMPurify.sanitize is unavailable (server pre-render) by temporarily clearing it, asserting processHtml/render return null instead of throwing. Raises patch coverage for the SSR guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(x-markdown): avoid jank when initial streaming output is non-streaming
The Prompts API documentation referenced the old type name `PromptProps`, which has been renamed to `PromptsItemType` in the source. Update the `items`, `onItemClick` and child item type references in both the Chinese and English docs accordingly. Closes #1863
* fix(x): refresh FileCard.List scroll ping state on items change When Attachments uses overflow="scrollX" and items are updated from outside (not via the internal upload flow), the scroll container's clientWidth stays the same while only scrollWidth grows. The ResizeObserver therefore does not fire and onScroll never happens, so checkPing() is not called and the prev/next scroll buttons never show. Add a useLayoutEffect that re-runs checkPing() via requestAnimationFrame whenever the rendered list (or overflow mode) changes, covering the controlled items-update case that ResizeObserver misses. Closes #1911 * refactor(x): memoize checkPing with useCallback to satisfy exhaustive-deps Address review feedback: wrap checkPing in React.useCallback (deps: [overflow]) and add it to the useLayoutEffect dependency array so the exhaustive-deps rule is satisfied. * revert: drop unnecessary useCallback wrapping of checkPing The project's biome config disables `useExhaustiveDependencies` (see biome.json), so wrapping checkPing in useCallback to satisfy the exhaustive-deps rule is unnecessary. Since checkPing only depends on `overflow` (already in the effect deps), the memoization changes nothing behaviorally. Revert to keep the code consistent with the file's style.
Custom tag inner content is treated as opaque text, but the protection logic only escaped blank lines (`\n\n`). Single newlines followed by block-level markdown (ordered/unordered lists, headings) were still parsed by marked, splitting the custom tag structure (e.g. the closing `</think>` was pulled into an `<li>`). Now every newline inside a custom tag is protected. Placeholders use Unicode Private Use Area sentinels instead of `__...__` so they are not interpreted as markdown emphasis when adjacent to text.
Protecting every newline inside custom tags changed the behavior of the existing `protectCustomTagNewlines` flag: block-level markdown (lists, headings, quotes) that used to render inside custom tags became opaque text, which is a breaking change for current users. Restore `protectCustomTagNewlines` to its original semantics (only protect blank-line paragraph breaks) and add a new opt-in flag `protectAllCustomTagNewlines` that protects every newline. Both default to false so existing behavior is unchanged.
The provider cache Map was created inside the component body, so it was re-created on every render and never actually cached the provider. As a result useXChat's requestHandlerRef pointed at a freshly-built provider whose request had never run, making abort() throw and unable to cancel the in-flight request when stopping a streaming response. Persist the cache with useRef so the provider instance stays stable across renders, aligning with the existing session-key demo. Fixes #1833.
…g-newline fix(x-markdown): protect all newlines inside custom tags (#1896)
* feat: Added right-click functionality for the Folder component (#1919) * feat: folder * feat: folder feat * feat: folder feat * feat: folder feat * feat: folder feat * chore: update version * feat: lingxi-dev * chore: update version * chore: update version * feat: lingxi-dev * feat: lingxi-dev * chore: update version * chore: update version * fix: exclude .dumi directory from TypeScript type checking * revert: restore .dumi tsconfig to original state * chore: update version * chore: update version * fix: add dumi type declarations to fix lint errors * feat: lint * feat: lint * feat: lint * feat: lint * chore: merge main to feature (#1923) * fix: fix ci * feat: folder * feat: folder feat * feat: folder feat * feat: folder feat * feat: folder feat * feat(x-markdown): add disableDefaultStyles to opt out of default tag styles Default tag styles are applied via descendant selectors on the `.x-markdown` container (e.g. `.x-markdown ul`, `.x-markdown li`, `.x-markdown code`), so they leak into elements rendered by custom components (such as antd `Collapse`) that are mounted inside the container, polluting their styles. Add a `disableDefaultStyles` prop: - `true` disables all built-in tag styles - an array (e.g. `['ul', 'ol', 'li']`) disables only the listed tags Implemented purely at the view layer: the root container gets `x-md-disable-all` or per-tag `x-md-disable-<tag>` classes, and each default rule in index.css is gated behind `:not(.x-md-disable-all):not(.x-md-disable-<tag>)`. The descendant selector strategy (so nested Markdown inherits defaults) is preserved; only the opt-out is added. Parser/Renderer core is untouched. closes #1908 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x): coerce string|number design tokens before arithmetic CI's antd version types `fontSizeHeading1/2` tokens as `string | number`, breaking `token.fontSizeHeading1 + 10` (TS2365) and `token.fontSizeHeading2 * 2` (TS2362). Wrap with `Number()` so the arithmetic is valid regardless of the token's declared type. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x-markdown): gate list markers by list container disable class `list-style` on `ul > li` / `ol > li` was gated only by `:not(.x-md-disable-li)`, so `disableDefaultStyles={['ul']}` left the bullets/numbers in place — users had to also pass `'li'`. Also gate these selectors by the container's disable class so disabling `ul`/`ol` removes their markers as expected, while keeping the `li` clause (no regression) and ordered/unordered independence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: fix ci * fix: fix ci * fix: avoid jank when initial streaming output is non-streaming Seed output with full input for non-streaming to prevent layout jitter. Signed-off-by: Waiter <waitersong2012@gmail.com> * Refactor streaming output handling in useStreaming hook Signed-off-by: Waiter <waitersong2012@gmail.com> * chore: update test snapshot * test(x-markdown): fix nodeName getter for happy-dom regression happy-dom (>= 20.10.x) returns '' from the Node.prototype.nodeName getter for element instances. DOMPurify reads nodeName via Node.prototype, so every element was treated as an unknown tag and stripped (keeping only text), causing 24 Renderer streamStatus tests to fail. Restore a spec-correct nodeName getter on Node.prototype in the jest setup. Test-env only; real browsers and jsdom are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x-markdown): guard DOMPurify against SSR (no DOM) The streaming refactor seeds non-streaming output synchronously on first render, so XMarkdown now renders during SSR too. DOMPurify's default export has no `sanitize` method without a `window`, so server pre-render crashed with "sanitize is not a function". Return null from processHtml when there is no DOM, letting the client hydrate — same as the pre-refactor server behavior. No new deps, no XSS exposure (server emits nothing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(x-markdown): cover SSR no-DOM guard in Renderer.processHtml Exercise the branch where DOMPurify.sanitize is unavailable (server pre-render) by temporarily clearing it, asserting processHtml/render return null instead of throwing. Raises patch coverage for the SSR guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: fix outdated type name in Prompts API table (#1915) The Prompts API documentation referenced the old type name `PromptProps`, which has been renamed to `PromptsItemType` in the source. Update the `items`, `onItemClick` and child item type references in both the Chinese and English docs accordingly. Closes #1863 * fix(x): refresh FileCard.List scroll ping state on items change (#1914) * fix(x): refresh FileCard.List scroll ping state on items change When Attachments uses overflow="scrollX" and items are updated from outside (not via the internal upload flow), the scroll container's clientWidth stays the same while only scrollWidth grows. The ResizeObserver therefore does not fire and onScroll never happens, so checkPing() is not called and the prev/next scroll buttons never show. Add a useLayoutEffect that re-runs checkPing() via requestAnimationFrame whenever the rendered list (or overflow mode) changes, covering the controlled items-update case that ResizeObserver misses. Closes #1911 * refactor(x): memoize checkPing with useCallback to satisfy exhaustive-deps Address review feedback: wrap checkPing in React.useCallback (deps: [overflow]) and add it to the useLayoutEffect dependency array so the exhaustive-deps rule is satisfied. * revert: drop unnecessary useCallback wrapping of checkPing The project's biome config disables `useExhaustiveDependencies` (see biome.json), so wrapping checkPing in useCallback to satisfy the exhaustive-deps rule is unnecessary. Since checkPing only depends on `overflow` (already in the effect deps), the memoization changes nothing behaviorally. Revert to keep the code consistent with the file's style. * chore: update version * feat: lingxi-dev * chore: update version * chore: update version * feat: lingxi-dev * feat: lingxi-dev * chore: update version * chore: update version * fix: exclude .dumi directory from TypeScript type checking * revert: restore .dumi tsconfig to original state * chore: update version * chore: update version * fix: add dumi type declarations to fix lint errors * feat: lint * feat: lint * feat: lint * feat: lint * fix: add missing DOMPurify import in Renderer.test.ts * feat: lint * ci: pinned dumi version to 2.4.28 (#1922) * feat: lingxi-dev --------- Signed-off-by: Waiter <waitersong2012@gmail.com> Co-authored-by: Div627 <chenyang_nn@163.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Div627 <54497462+Div627@users.noreply.github.com> Co-authored-by: Waiter <waitersong2012@gmail.com> Co-authored-by: Ryker_Feng <90562015+18062706139fcz@users.noreply.github.com> Co-authored-by: 遇见同学 <1875694521@qq.com> * chore: merge mian to feature (#1924) * fix: fix ci * feat: folder * feat: folder feat * feat: folder feat * feat: folder feat * feat: folder feat * feat(x-markdown): add disableDefaultStyles to opt out of default tag styles Default tag styles are applied via descendant selectors on the `.x-markdown` container (e.g. `.x-markdown ul`, `.x-markdown li`, `.x-markdown code`), so they leak into elements rendered by custom components (such as antd `Collapse`) that are mounted inside the container, polluting their styles. Add a `disableDefaultStyles` prop: - `true` disables all built-in tag styles - an array (e.g. `['ul', 'ol', 'li']`) disables only the listed tags Implemented purely at the view layer: the root container gets `x-md-disable-all` or per-tag `x-md-disable-<tag>` classes, and each default rule in index.css is gated behind `:not(.x-md-disable-all):not(.x-md-disable-<tag>)`. The descendant selector strategy (so nested Markdown inherits defaults) is preserved; only the opt-out is added. Parser/Renderer core is untouched. closes #1908 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x): coerce string|number design tokens before arithmetic CI's antd version types `fontSizeHeading1/2` tokens as `string | number`, breaking `token.fontSizeHeading1 + 10` (TS2365) and `token.fontSizeHeading2 * 2` (TS2362). Wrap with `Number()` so the arithmetic is valid regardless of the token's declared type. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x-markdown): gate list markers by list container disable class `list-style` on `ul > li` / `ol > li` was gated only by `:not(.x-md-disable-li)`, so `disableDefaultStyles={['ul']}` left the bullets/numbers in place — users had to also pass `'li'`. Also gate these selectors by the container's disable class so disabling `ul`/`ol` removes their markers as expected, while keeping the `li` clause (no regression) and ordered/unordered independence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: fix ci * fix: fix ci * fix: avoid jank when initial streaming output is non-streaming Seed output with full input for non-streaming to prevent layout jitter. Signed-off-by: Waiter <waitersong2012@gmail.com> * Refactor streaming output handling in useStreaming hook Signed-off-by: Waiter <waitersong2012@gmail.com> * chore: update test snapshot * test(x-markdown): fix nodeName getter for happy-dom regression happy-dom (>= 20.10.x) returns '' from the Node.prototype.nodeName getter for element instances. DOMPurify reads nodeName via Node.prototype, so every element was treated as an unknown tag and stripped (keeping only text), causing 24 Renderer streamStatus tests to fail. Restore a spec-correct nodeName getter on Node.prototype in the jest setup. Test-env only; real browsers and jsdom are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x-markdown): guard DOMPurify against SSR (no DOM) The streaming refactor seeds non-streaming output synchronously on first render, so XMarkdown now renders during SSR too. DOMPurify's default export has no `sanitize` method without a `window`, so server pre-render crashed with "sanitize is not a function". Return null from processHtml when there is no DOM, letting the client hydrate — same as the pre-refactor server behavior. No new deps, no XSS exposure (server emits nothing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(x-markdown): cover SSR no-DOM guard in Renderer.processHtml Exercise the branch where DOMPurify.sanitize is unavailable (server pre-render) by temporarily clearing it, asserting processHtml/render return null instead of throwing. Raises patch coverage for the SSR guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: fix outdated type name in Prompts API table (#1915) The Prompts API documentation referenced the old type name `PromptProps`, which has been renamed to `PromptsItemType` in the source. Update the `items`, `onItemClick` and child item type references in both the Chinese and English docs accordingly. Closes #1863 * fix(x): refresh FileCard.List scroll ping state on items change (#1914) * fix(x): refresh FileCard.List scroll ping state on items change When Attachments uses overflow="scrollX" and items are updated from outside (not via the internal upload flow), the scroll container's clientWidth stays the same while only scrollWidth grows. The ResizeObserver therefore does not fire and onScroll never happens, so checkPing() is not called and the prev/next scroll buttons never show. Add a useLayoutEffect that re-runs checkPing() via requestAnimationFrame whenever the rendered list (or overflow mode) changes, covering the controlled items-update case that ResizeObserver misses. Closes #1911 * refactor(x): memoize checkPing with useCallback to satisfy exhaustive-deps Address review feedback: wrap checkPing in React.useCallback (deps: [overflow]) and add it to the useLayoutEffect dependency array so the exhaustive-deps rule is satisfied. * revert: drop unnecessary useCallback wrapping of checkPing The project's biome config disables `useExhaustiveDependencies` (see biome.json), so wrapping checkPing in useCallback to satisfy the exhaustive-deps rule is unnecessary. Since checkPing only depends on `overflow` (already in the effect deps), the memoization changes nothing behaviorally. Revert to keep the code consistent with the file's style. * chore: update version * feat: lingxi-dev * chore: update version * chore: update version * feat: lingxi-dev * feat: lingxi-dev * chore: update version * chore: update version * fix: exclude .dumi directory from TypeScript type checking * revert: restore .dumi tsconfig to original state * chore: update version * chore: update version * fix: add dumi type declarations to fix lint errors * feat: lint * feat: lint * feat: lint * feat: lint * fix: add missing DOMPurify import in Renderer.test.ts * feat: lint * ci: pinned dumi version to 2.4.28 (#1922) * feat: lingxi-dev * chore: merge --------- Signed-off-by: Waiter <waitersong2012@gmail.com> Co-authored-by: Div627 <chenyang_nn@163.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Div627 <54497462+Div627@users.noreply.github.com> Co-authored-by: Waiter <waitersong2012@gmail.com> Co-authored-by: Ryker_Feng <90562015+18062706139fcz@users.noreply.github.com> Co-authored-by: 遇见同学 <1875694521@qq.com> * feat: merge * feat: lingxi-dev * feat: lingxi-dev * chore: feature merge main (#1927) * fix: fix ci * feat(x-markdown): add disableDefaultStyles to opt out of default tag styles Default tag styles are applied via descendant selectors on the `.x-markdown` container (e.g. `.x-markdown ul`, `.x-markdown li`, `.x-markdown code`), so they leak into elements rendered by custom components (such as antd `Collapse`) that are mounted inside the container, polluting their styles. Add a `disableDefaultStyles` prop: - `true` disables all built-in tag styles - an array (e.g. `['ul', 'ol', 'li']`) disables only the listed tags Implemented purely at the view layer: the root container gets `x-md-disable-all` or per-tag `x-md-disable-<tag>` classes, and each default rule in index.css is gated behind `:not(.x-md-disable-all):not(.x-md-disable-<tag>)`. The descendant selector strategy (so nested Markdown inherits defaults) is preserved; only the opt-out is added. Parser/Renderer core is untouched. closes #1908 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x): coerce string|number design tokens before arithmetic CI's antd version types `fontSizeHeading1/2` tokens as `string | number`, breaking `token.fontSizeHeading1 + 10` (TS2365) and `token.fontSizeHeading2 * 2` (TS2362). Wrap with `Number()` so the arithmetic is valid regardless of the token's declared type. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x-markdown): gate list markers by list container disable class `list-style` on `ul > li` / `ol > li` was gated only by `:not(.x-md-disable-li)`, so `disableDefaultStyles={['ul']}` left the bullets/numbers in place — users had to also pass `'li'`. Also gate these selectors by the container's disable class so disabling `ul`/`ol` removes their markers as expected, while keeping the `li` clause (no regression) and ordered/unordered independence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: fix ci * fix: fix ci * fix: avoid jank when initial streaming output is non-streaming Seed output with full input for non-streaming to prevent layout jitter. Signed-off-by: Waiter <waitersong2012@gmail.com> * Refactor streaming output handling in useStreaming hook Signed-off-by: Waiter <waitersong2012@gmail.com> * chore: update test snapshot * test(x-markdown): fix nodeName getter for happy-dom regression happy-dom (>= 20.10.x) returns '' from the Node.prototype.nodeName getter for element instances. DOMPurify reads nodeName via Node.prototype, so every element was treated as an unknown tag and stripped (keeping only text), causing 24 Renderer streamStatus tests to fail. Restore a spec-correct nodeName getter on Node.prototype in the jest setup. Test-env only; real browsers and jsdom are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x-markdown): guard DOMPurify against SSR (no DOM) The streaming refactor seeds non-streaming output synchronously on first render, so XMarkdown now renders during SSR too. DOMPurify's default export has no `sanitize` method without a `window`, so server pre-render crashed with "sanitize is not a function". Return null from processHtml when there is no DOM, letting the client hydrate — same as the pre-refactor server behavior. No new deps, no XSS exposure (server emits nothing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(x-markdown): cover SSR no-DOM guard in Renderer.processHtml Exercise the branch where DOMPurify.sanitize is unavailable (server pre-render) by temporarily clearing it, asserting processHtml/render return null instead of throwing. Raises patch coverage for the SSR guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: fix outdated type name in Prompts API table (#1915) The Prompts API documentation referenced the old type name `PromptProps`, which has been renamed to `PromptsItemType` in the source. Update the `items`, `onItemClick` and child item type references in both the Chinese and English docs accordingly. Closes #1863 * fix(x): refresh FileCard.List scroll ping state on items change (#1914) * fix(x): refresh FileCard.List scroll ping state on items change When Attachments uses overflow="scrollX" and items are updated from outside (not via the internal upload flow), the scroll container's clientWidth stays the same while only scrollWidth grows. The ResizeObserver therefore does not fire and onScroll never happens, so checkPing() is not called and the prev/next scroll buttons never show. Add a useLayoutEffect that re-runs checkPing() via requestAnimationFrame whenever the rendered list (or overflow mode) changes, covering the controlled items-update case that ResizeObserver misses. Closes #1911 * refactor(x): memoize checkPing with useCallback to satisfy exhaustive-deps Address review feedback: wrap checkPing in React.useCallback (deps: [overflow]) and add it to the useLayoutEffect dependency array so the exhaustive-deps rule is satisfied. * revert: drop unnecessary useCallback wrapping of checkPing The project's biome config disables `useExhaustiveDependencies` (see biome.json), so wrapping checkPing in useCallback to satisfy the exhaustive-deps rule is unnecessary. Since checkPing only depends on `overflow` (already in the effect deps), the memoization changes nothing behaviorally. Revert to keep the code consistent with the file's style. * ci: pinned dumi version to 2.4.28 (#1922) * docs: cache chat provider with useRef in x-conversations demos (#1925) The provider cache Map was created inside the component body, so it was re-created on every render and never actually cached the provider. As a result useXChat's requestHandlerRef pointed at a freshly-built provider whose request had never run, making abort() throw and unable to cancel the in-flight request when stopping a streaming response. Persist the cache with useRef so the provider instance stays stable across renders, aligning with the existing session-key demo. Fixes #1833. * feat: merge * feat: lingxi-dev * feat: lingxi-dev --------- Signed-off-by: Waiter <waitersong2012@gmail.com> Co-authored-by: Div627 <chenyang_nn@163.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Div627 <54497462+Div627@users.noreply.github.com> Co-authored-by: Waiter <waitersong2012@gmail.com> Co-authored-by: Ryker_Feng <90562015+18062706139fcz@users.noreply.github.com> Co-authored-by: 遇见同学 <1875694521@qq.com> * chore: feature merge main (#1928) * fix: fix ci * feat(x-markdown): add disableDefaultStyles to opt out of default tag styles Default tag styles are applied via descendant selectors on the `.x-markdown` container (e.g. `.x-markdown ul`, `.x-markdown li`, `.x-markdown code`), so they leak into elements rendered by custom components (such as antd `Collapse`) that are mounted inside the container, polluting their styles. Add a `disableDefaultStyles` prop: - `true` disables all built-in tag styles - an array (e.g. `['ul', 'ol', 'li']`) disables only the listed tags Implemented purely at the view layer: the root container gets `x-md-disable-all` or per-tag `x-md-disable-<tag>` classes, and each default rule in index.css is gated behind `:not(.x-md-disable-all):not(.x-md-disable-<tag>)`. The descendant selector strategy (so nested Markdown inherits defaults) is preserved; only the opt-out is added. Parser/Renderer core is untouched. closes #1908 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x): coerce string|number design tokens before arithmetic CI's antd version types `fontSizeHeading1/2` tokens as `string | number`, breaking `token.fontSizeHeading1 + 10` (TS2365) and `token.fontSizeHeading2 * 2` (TS2362). Wrap with `Number()` so the arithmetic is valid regardless of the token's declared type. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x-markdown): gate list markers by list container disable class `list-style` on `ul > li` / `ol > li` was gated only by `:not(.x-md-disable-li)`, so `disableDefaultStyles={['ul']}` left the bullets/numbers in place — users had to also pass `'li'`. Also gate these selectors by the container's disable class so disabling `ul`/`ol` removes their markers as expected, while keeping the `li` clause (no regression) and ordered/unordered independence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: fix ci * fix: fix ci * fix: avoid jank when initial streaming output is non-streaming Seed output with full input for non-streaming to prevent layout jitter. Signed-off-by: Waiter <waitersong2012@gmail.com> * Refactor streaming output handling in useStreaming hook Signed-off-by: Waiter <waitersong2012@gmail.com> * chore: update test snapshot * test(x-markdown): fix nodeName getter for happy-dom regression happy-dom (>= 20.10.x) returns '' from the Node.prototype.nodeName getter for element instances. DOMPurify reads nodeName via Node.prototype, so every element was treated as an unknown tag and stripped (keeping only text), causing 24 Renderer streamStatus tests to fail. Restore a spec-correct nodeName getter on Node.prototype in the jest setup. Test-env only; real browsers and jsdom are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(x-markdown): guard DOMPurify against SSR (no DOM) The streaming refactor seeds non-streaming output synchronously on first render, so XMarkdown now renders during SSR too. DOMPurify's default export has no `sanitize` method without a `window`, so server pre-render crashed with "sanitize is not a function". Return null from processHtml when there is no DOM, letting the client hydrate — same as the pre-refactor server behavior. No new deps, no XSS exposure (server emits nothing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(x-markdown): cover SSR no-DOM guard in Renderer.processHtml Exercise the branch where DOMPurify.sanitize is unavailable (server pre-render) by temporarily clearing it, asserting processHtml/render return null instead of throwing. Raises patch coverage for the SSR guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: fix outdated type name in Prompts API table (#1915) The Prompts API documentation referenced the old type name `PromptProps`, which has been renamed to `PromptsItemType` in the source. Update the `items`, `onItemClick` and child item type references in both the Chinese and English docs accordingly. Closes #1863 * fix(x): refresh FileCard.List scroll ping state on items change (#1914) * fix(x): refresh FileCard.List scroll ping state on items change When Attachments uses overflow="scrollX" and items are updated from outside (not via the internal upload flow), the scroll container's clientWidth stays the same while only scrollWidth grows. The ResizeObserver therefore does not fire and onScroll never happens, so checkPing() is not called and the prev/next scroll buttons never show. Add a useLayoutEffect that re-runs checkPing() via requestAnimationFrame whenever the rendered list (or overflow mode) changes, covering the controlled items-update case that ResizeObserver misses. Closes #1911 * refactor(x): memoize checkPing with useCallback to satisfy exhaustive-deps Address review feedback: wrap checkPing in React.useCallback (deps: [overflow]) and add it to the useLayoutEffect dependency array so the exhaustive-deps rule is satisfied. * revert: drop unnecessary useCallback wrapping of checkPing The project's biome config disables `useExhaustiveDependencies` (see biome.json), so wrapping checkPing in useCallback to satisfy the exhaustive-deps rule is unnecessary. Since checkPing only depends on `overflow` (already in the effect deps), the memoization changes nothing behaviorally. Revert to keep the code consistent with the file's style. * fix(x-markdown): protect all newlines inside custom tags (#1896) Custom tag inner content is treated as opaque text, but the protection logic only escaped blank lines (`\n\n`). Single newlines followed by block-level markdown (ordered/unordered lists, headings) were still parsed by marked, splitting the custom tag structure (e.g. the closing `</think>` was pulled into an `<li>`). Now every newline inside a custom tag is protected. Placeholders use Unicode Private Use Area sentinels instead of `__...__` so they are not interpreted as markdown emphasis when adjacent to text. * fix(x-markdown): split full-newline protection into a separate flag Protecting every newline inside custom tags changed the behavior of the existing `protectCustomTagNewlines` flag: block-level markdown (lists, headings, quotes) that used to render inside custom tags became opaque text, which is a breaking change for current users. Restore `protectCustomTagNewlines` to its original semantics (only protect blank-line paragraph breaks) and add a new opt-in flag `protectAllCustomTagNewlines` that protects every newline. Both default to false so existing behavior is unchanged. * ci: pinned dumi version to 2.4.28 (#1922) * refactor(x-markdown): clarify custom tag block markdown option * refactor(x-markdown): align internal flag name with public option * docs: cache chat provider with useRef in x-conversations demos (#1925) The provider cache Map was created inside the component body, so it was re-created on every render and never actually cached the provider. As a result useXChat's requestHandlerRef pointed at a freshly-built provider whose request had never run, making abort() throw and unable to cancel the in-flight request when stopping a streaming response. Persist the cache with useRef so the provider instance stays stable across renders, aligning with the existing session-key demo. Fixes #1833. * feat: merge * feat: lingxi-dev * feat: lingxi-dev --------- Signed-off-by: Waiter <waitersong2012@gmail.com> Co-authored-by: Div627 <chenyang_nn@163.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Div627 <54497462+Div627@users.noreply.github.com> Co-authored-by: Waiter <waitersong2012@gmail.com> Co-authored-by: Ryker_Feng <90562015+18062706139fcz@users.noreply.github.com> Co-authored-by: ryker <2279549769@qq.com> Co-authored-by: 遇见同学 <1875694521@qq.com> --------- Signed-off-by: Waiter <waitersong2012@gmail.com> Co-authored-by: Div627 <chenyang_nn@163.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Div627 <54497462+Div627@users.noreply.github.com> Co-authored-by: Waiter <waitersong2012@gmail.com> Co-authored-by: Ryker_Feng <90562015+18062706139fcz@users.noreply.github.com> Co-authored-by: 遇见同学 <1875694521@qq.com> Co-authored-by: ryker <2279549769@qq.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces several enhancements and fixes across the repository, notably adding a contextMenu feature and ref methods (getNode, updateNode, deleteNode, addNode) to the Folder component, implementing a disableCustomTagBlockMarkdown option and a disableDefaultStyles prop in XMarkdown, and patching DOMPurify to resolve a compatibility issue with happy-dom. Feedback on the changes suggests refactoring the Folder component's DirectoryTree to use useMemo instead of writing to a useRef during render to avoid React anti-patterns and potential memory leaks. Additionally, it is recommended to lazily initialize providerCachesRef in the conversations demo to prevent unnecessary Map instantiations on every render.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const [contextMenuItems, setContextMenuItems] = useState<MenuProps['items']>(undefined); | ||
|
|
||
| // Store all original node data indexed by key for quick lookup | ||
| const nodeDataMapRef = useRef<Map<string, FolderTreeData>>(new Map()); |
There was a problem hiding this comment.
Writing to a useRef during render is a React anti-pattern and can lead to unpredictable behavior in concurrent rendering or strict mode. Additionally, since the map is never cleared, it can cause a memory leak when nodes are dynamically updated or deleted. Consider using useMemo to build the node data map from treeData instead.
// Store all original node data indexed by key for quick lookup
const nodeDataMap = React.useMemo(() => {
const map = new Map<string, FolderTreeData>();
const traverse = (nodes?: FolderTreeData[], parentSegments: string[] = []) => {
nodes?.forEach((node) => {
const pathSegments = [...parentSegments, node.path];
const fullPath = pathSegments.join('/').replace(/^\/+/, '');
map.set(fullPath, node);
if (node.children) {
traverse(node.children, pathSegments);
}
});
};
traverse(treeData);
return map;
}, [treeData]);
| const handleRightClick: TreeProps['onRightClick'] = (info) => { | ||
| const { node } = info; | ||
| const nodeKey = node.key as string; | ||
| const originalNode = nodeDataMapRef.current.get(nodeKey); |
| // 提供者缓存:为每个会话缓存独立的聊天提供者实例 | ||
| // Provider cache: cache independent chat provider instances for each conversation | ||
| const providerCaches = new Map<string, DeepSeekChatProvider>(); | ||
| const providerCachesRef = useRef(new Map<string, DeepSeekChatProvider>()); |
There was a problem hiding this comment.
To avoid instantiating a new Map() on every single render, use lazy initialization for the ref. This also aligns with the implementation pattern used in the other demo files (e.g., with-x-chat.tsx).
| const providerCachesRef = useRef(new Map<string, DeepSeekChatProvider>()); | |
| const providerCachesRef = useRef<Map<string, DeepSeekChatProvider> | null>(null); |
| // 提供者工厂:根据会话key创建或获取对应的聊天提供者 | ||
| // Provider factory: create or get corresponding chat provider based on conversation key | ||
| const providerFactory = (conversationKey: string) => { | ||
| const providerCaches = providerCachesRef.current; |
|
This comment is automatically generated by the x-markdown performance CI. |
Deploying ant-design-x with
|
| Latest commit: |
a270320
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://2af03ae3.ant-design-x.pages.dev |
size-limit report 📦
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## feature #1930 +/- ##
===========================================
- Coverage 97.62% 97.09% -0.53%
===========================================
Files 158 158
Lines 5547 5711 +164
Branches 1621 1676 +55
===========================================
+ Hits 5415 5545 +130
- Misses 130 164 +34
Partials 2 2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
中文版模板 / Chinese template
🤔 This is a ...
🔗 Related Issues
💡 Background and Solution
📝 Change Log