Skip to content

Commit

Permalink
chore: improve CodeBlock performance with debouncing
Browse files Browse the repository at this point in the history
Addresses suggestions from @ellipsis-dev:
- Add debouncing to updateCopyButtonPosition for better scroll performance
- Add error handling for clipboard operations with visual feedback
- Fix React hook dependency warnings

Signed-off-by: Eric Wheeler <[email protected]>
  • Loading branch information
Eric Wheeler committed Mar 3, 2025
1 parent 9fd7b32 commit 3e57819
Showing 1 changed file with 24 additions and 12 deletions.
36 changes: 24 additions & 12 deletions webview-ui/src/components/common/CodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { memo, useEffect, useRef, useState } from "react"
import { memo, useEffect, useRef, useState, useCallback } from "react"
import debounce from "debounce"
import { useRemark } from "react-remark"
import rehypeHighlight, { Options } from "rehype-highlight"
import styled from "styled-components"
Expand Down Expand Up @@ -204,7 +205,7 @@ const CodeBlock = memo(({ source, language, preStyle }: CodeBlockProps) => {
},
})

const updateCopyButtonPosition = (forceShow = false) => {
const updateCopyButtonPosition = useCallback((forceShow = false) => {
const codeBlock = codeBlockRef.current
if (!codeBlock) return

Expand All @@ -226,11 +227,12 @@ const CodeBlock = memo(({ source, language, preStyle }: CodeBlockProps) => {
codeBlock.style.setProperty("--copy-button-top", `${topPosition}px`)
codeBlock.style.setProperty("--copy-button-right", `${rightPosition}px`)
}
}
}, [])

useEffect(() => {
const handleScroll = () => updateCopyButtonPosition()
const handleResize = () => updateCopyButtonPosition()
const debouncedUpdate = debounce(updateCopyButtonPosition, 10)
const handleScroll = () => debouncedUpdate()
const handleResize = () => debouncedUpdate()

const scrollContainer = document.querySelector('[data-virtuoso-scroller="true"]')
if (scrollContainer) {
Expand All @@ -244,25 +246,35 @@ const CodeBlock = memo(({ source, language, preStyle }: CodeBlockProps) => {
scrollContainer.removeEventListener("scroll", handleScroll)
window.removeEventListener("resize", handleResize)
}
debouncedUpdate.clear()
}
}, [])
}, [updateCopyButtonPosition])

// Update button position when content changes
useEffect(() => {
if (reactContent) {
// Small delay to ensure content is rendered
setTimeout(() => updateCopyButtonPosition(), 0)
setTimeout(updateCopyButtonPosition, 0)
}
}, [reactContent])
}, [reactContent, updateCopyButtonPosition])

const [copyError, setCopyError] = useState(false)

const handleCopy = (e: React.MouseEvent) => {
e.stopPropagation()
if (source) {
// Extract code content from markdown code block
const codeContent = source.replace(/^```[\s\S]*?\n([\s\S]*?)```$/m, "$1").trim()
navigator.clipboard.writeText(codeContent)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
try {
navigator.clipboard.writeText(codeContent)
setCopied(true)
setCopyError(false)
setTimeout(() => setCopied(false), 2000)
} catch (error) {
console.error("Failed to copy to clipboard:", error)
setCopyError(true)
setTimeout(() => setCopyError(false), 2000)
}
}
}

Expand All @@ -280,7 +292,7 @@ const CodeBlock = memo(({ source, language, preStyle }: CodeBlockProps) => {
onMouseEnter={() => updateCopyButtonPosition(true)}
onMouseLeave={() => updateCopyButtonPosition()}>
<CopyButton onClick={handleCopy} title="Copy code">
<span className={`codicon codicon-${copied ? "check" : "copy"}`} />
<span className={`codicon codicon-${copied ? "check" : copyError ? "error" : "copy"}`} />
</CopyButton>
</CopyButtonWrapper>
</CodeBlockContainer>
Expand Down

0 comments on commit 3e57819

Please sign in to comment.