diff --git a/CHANGELOG.md b/CHANGELOG.md index 30f2b2af9..e41a1ba65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Hide version upgrade toast for askgithub deployment (`EXPERIMENT_ASK_GH_ENABLED`). [#931](https://github.com/sourcebot-dev/sourcebot/pull/931) ### Fixed -- Fixed text inside angle brackets (e.g., ``) being hidden in chat prompt display due to HTML parsing. [#929](https://github.com/sourcebot-dev/sourcebot/pull/929) +- Fixed text inside angle brackets (e.g., ``) being hidden in chat prompt display due to HTML parsing. [#929](https://github.com/sourcebot-dev/sourcebot/pull/929) [#932](https://github.com/sourcebot-dev/sourcebot/pull/932) ## [4.11.7] - 2026-02-23 diff --git a/packages/web/src/features/chat/components/chatThread/chatThreadListItem.tsx b/packages/web/src/features/chat/components/chatThread/chatThreadListItem.tsx index ce329c73d..1734a04d5 100644 --- a/packages/web/src/features/chat/components/chatThread/chatThreadListItem.tsx +++ b/packages/web/src/features/chat/components/chatThread/chatThreadListItem.tsx @@ -334,7 +334,7 @@ const ChatThreadListItemComponent = forwardRef diff --git a/packages/web/src/features/chat/components/chatThread/markdownRenderer.tsx b/packages/web/src/features/chat/components/chatThread/markdownRenderer.tsx index 809481d09..b7e5daeb5 100644 --- a/packages/web/src/features/chat/components/chatThread/markdownRenderer.tsx +++ b/packages/web/src/features/chat/components/chatThread/markdownRenderer.tsx @@ -80,6 +80,27 @@ function remarkReferencesPlugin() { } } +/** + * A remark plugin that converts `html` MDAST nodes into `text` nodes, + * preserving angle-bracketed content like `` as visible text. Without this, + * `` is parsed as an HTML tag and then stripped by sanitization. + * + * This plugin must run BEFORE remarkReferencesPlugin so that the file-reference + * HTML nodes created by that plugin are left intact for rehypeRaw to process. + */ +function remarkPreserveHtml() { + return function (tree: Nodes) { + visit(tree, 'html', (node, index, parent) => { + if (index !== undefined && parent && 'children' in parent) { + (parent.children as Nodes[])[index] = { + type: 'text', + value: (node as { value: string }).value, + }; + } + }); + }; +} + const remarkTocExtractor = () => { return function (tree: Nodes) { visit(tree, 'heading', (node: Heading) => { @@ -102,32 +123,27 @@ interface MarkdownRendererProps { content: string; className?: string; /** - * When true, disables raw HTML parsing. This prevents text like `` from - * being interpreted as HTML tags. Use this for user-provided content that - * shouldn't contain embedded HTML. + * When true, angle-bracketed text like `` is preserved as visible text + * instead of being parsed as HTML. File references (@file:{...}) are unaffected. */ - disableRawHtml?: boolean; + escapeHtml?: boolean; } -const MarkdownRendererComponent = forwardRef(({ content, className, disableRawHtml = false }, ref) => { +const MarkdownRendererComponent = forwardRef(({ content, className, escapeHtml = false }, ref) => { const router = useRouter(); const remarkPlugins = useMemo((): PluggableList => { return [ remarkGfm, + ...(escapeHtml ? [remarkPreserveHtml] : []), remarkReferencesPlugin, remarkTocExtractor, ]; - }, []); + }, [escapeHtml]); const rehypePlugins = useMemo((): PluggableList => { - const plugins: PluggableList = []; - - if (!disableRawHtml) { - plugins.push(rehypeRaw); - } - - plugins.push( + return [ + rehypeRaw, [ rehypeSanitize, { @@ -140,10 +156,8 @@ const MarkdownRendererComponent = forwardRef { if (node?.properties && node.properties.isBlock === true) {