diff --git a/src/core/ClaudeDev.ts b/src/core/ClaudeDev.ts
index e08640d..8547a2c 100644
--- a/src/core/ClaudeDev.ts
+++ b/src/core/ClaudeDev.ts
@@ -771,20 +771,38 @@ export class ClaudeDev {
break
}
let content = block.content
- if (block.partial && content) {
- // Remove partial XML tag at the very end of the content
+ if (content) {
+ // (have to do this for partial and complete since sending content in thinking tags to markdown renderer will automatically be removed)
+ // Remove end substrings of (with optional line break after) and (with optional line break before)
+ // - Needs to be separate since we dont want to remove the line break before the first tag
+ // - Needs to happen before the xml parsing below
+ content = content.replace(/\s?/g, "")
+ content = content.replace(/\s?<\/thinking>/g, "")
+
+ // Remove partial XML tag at the very end of the content (for tool use and thinking tags)
+ // (prevents scrollview from jumping when tags are automatically removed)
const lastOpenBracketIndex = content.lastIndexOf("<")
if (lastOpenBracketIndex !== -1) {
const possibleTag = content.slice(lastOpenBracketIndex)
- // Check if there's a '>' after the last '<' (i.e., if the tag is complete)
+ // Check if there's a '>' after the last '<' (i.e., if the tag is complete) (complete thinking and tool tags will have been removed by now)
const hasCloseBracket = possibleTag.includes(">")
if (!hasCloseBracket) {
// Extract the potential tag name
- const tagContent = possibleTag.slice(1).trim()
+ let tagContent: string
+ if (possibleTag.startsWith("")) {
+ tagContent = possibleTag.slice(2).trim()
+ } else {
+ tagContent = possibleTag.slice(1).trim()
+ }
// Check if tagContent is likely an incomplete tag name (letters and underscores only)
const isLikelyTagName = /^[a-zA-Z_]+$/.test(tagContent)
+ // Preemptively remove < or to keep from these artifacts showing up in chat (also handles closing thinking tags)
+ const isOpeningOrClosing = possibleTag === "<" || possibleTag === ""
// If the tag is incomplete and at the end, remove it from the content
- if (isLikelyTagName) {
+ if (isOpeningOrClosing || isLikelyTagName) {
content = content.slice(0, lastOpenBracketIndex).trim()
}
}
diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx
index d375aa8..4ce7c03 100644
--- a/webview-ui/src/components/chat/ChatRow.tsx
+++ b/webview-ui/src/components/chat/ChatRow.tsx
@@ -800,25 +800,9 @@ const ProgressIndicator = () => (
)
const Markdown = memo(({ markdown }: { markdown?: string }) => {
- const withoutThinkingTags = useMemo(() => {
- if (!markdown) return ""
-
- let processed = markdown
-
- // Remove end substrings of (with optional line break after) and (with optional line break before)
- // Needs to be separate since we dont want to remove the line break before the first tag
- processed = processed.replace(/\s?/g, "")
- processed = processed.replace(/\s?<\/thinking>/g, "")
-
- return processed
- }, [markdown])
-
return (
-
+
)
})