diff --git a/site/src/components/ai-elements/response.stories.tsx b/site/src/components/ai-elements/response.stories.tsx
index cef2259010..5a2392567c 100644
--- a/site/src/components/ai-elements/response.stories.tsx
+++ b/site/src/components/ai-elements/response.stories.tsx
@@ -1,4 +1,5 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
+import { expect, within } from "storybook/test";
import { Response } from "./response";
const sampleMarkdown = `
@@ -66,3 +67,39 @@ export const MarkdownAndLinksLight: Story = {
theme: "light",
},
};
+
+// Verifies that JSX-like syntax in LLM output is preserved as
+// escaped text rather than being swallowed by the HTML pipeline.
+const jsxProseMarkdown = `
+\`getLineAnnotations\` depends on \`activeCommentBox\` which could shift.
+
+
+
+The props that might change on every \`RemoteDiffPanel\` re-render:
+- \`isLoading\` only during refetch
+- \`getLineAnnotations\` only when \`activeCommentBox\` changes
+`;
+
+export const JsxInProse: Story = {
+ args: {
+ children: jsxProseMarkdown,
+ },
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+ // These strings live inside the JSX block.
+ // Without the rehype-raw fix they are silently eaten by the
+ // HTML sanitizer and never reach the DOM.
+ // The tag name itself is the token most likely to be consumed
+ // by HTML parsing, so assert it explicitly.
+ const tagName = await canvas.findByText(/, "children"> {
@@ -13,6 +18,16 @@ interface ResponseProps extends Omit, "children"> {
urlTransform?: UrlTransform;
}
+// Omit rehype-raw so HTML-like syntax in LLM output is rendered as
+// escaped text instead of being parsed by the HTML5 engine. Without
+// this, JSX fragments such as are
+// consumed by rehype-raw and then stripped by rehype-sanitize,
+// silently destroying content mid-stream.
+const chatRehypePlugins = [
+ defaultRehypePlugins.sanitize,
+ defaultRehypePlugins.harden,
+];
+
const fileViewerCSS =
"pre, [data-line], [data-diffs-header] { background-color: transparent !important; }";
@@ -241,6 +256,7 @@ export const Response = ({
controls={false}
components={components}
urlTransform={urlTransform}
+ rehypePlugins={chatRehypePlugins}
>
{children}