|
1 | 1 | import { memo, useEffect, useRef, useCallback, useState } from "react" |
2 | 2 | import styled from "styled-components" |
3 | 3 | import { useCopyToClipboard } from "@src/utils/clipboard" |
| 4 | +import { useDebounceEffect } from "@src/utils/useDebounceEffect" |
4 | 5 | import { getHighlighter, isLanguageLoaded, normalizeLanguage } from "@src/utils/highlighter" |
5 | 6 | import type { ShikiTransformer } from "shiki" |
6 | 7 | import { toJsxRuntime } from "hast-util-to-jsx-runtime" |
@@ -290,14 +291,19 @@ const CodeBlock = memo( |
290 | 291 | }, [source, currentLanguage, collapsedHeight]) |
291 | 292 |
|
292 | 293 | // Check if content height exceeds collapsed height whenever content changes |
293 | | - useEffect(() => { |
294 | | - const codeBlock = codeBlockRef.current |
| 294 | + // Debounced to avoid excessive DOM measurements during rapid scrolling |
| 295 | + useDebounceEffect( |
| 296 | + () => { |
| 297 | + const codeBlock = codeBlockRef.current |
295 | 298 |
|
296 | | - if (codeBlock) { |
297 | | - const actualHeight = codeBlock.scrollHeight |
298 | | - setShowCollapseButton(actualHeight >= WINDOW_SHADE_SETTINGS.collapsedHeight) |
299 | | - } |
300 | | - }, [highlightedCode]) |
| 299 | + if (codeBlock) { |
| 300 | + const actualHeight = codeBlock.scrollHeight |
| 301 | + setShowCollapseButton(actualHeight >= WINDOW_SHADE_SETTINGS.collapsedHeight) |
| 302 | + } |
| 303 | + }, |
| 304 | + 100, // 100ms debounce delay |
| 305 | + [highlightedCode], |
| 306 | + ) |
301 | 307 |
|
302 | 308 | // Ref to track if user was scrolled up *before* the source update |
303 | 309 | // potentially changes scrollHeight |
@@ -658,29 +664,18 @@ const CodeBlock = memo( |
658 | 664 | side="top"> |
659 | 665 | <CodeBlockButton |
660 | 666 | onClick={() => { |
661 | | - // Get the current code block element |
662 | | - const codeBlock = codeBlockRef.current // Capture ref early |
663 | 667 | // Toggle window shade state |
664 | 668 | setWindowShade(!windowShade) |
665 | 669 |
|
666 | 670 | // Clear any previous timeouts |
667 | 671 | if (collapseTimeout1Ref.current) clearTimeout(collapseTimeout1Ref.current) |
668 | 672 | if (collapseTimeout2Ref.current) clearTimeout(collapseTimeout2Ref.current) |
669 | 673 |
|
670 | | - // After UI updates, ensure code block is visible and update button position |
| 674 | + // After UI transition completes, update button position |
| 675 | + // Let ChatView/Virtuoso handle scrolling to avoid conflicts |
671 | 676 | collapseTimeout1Ref.current = setTimeout( |
672 | 677 | () => { |
673 | | - if (codeBlock) { |
674 | | - // Check if codeBlock element still exists |
675 | | - codeBlock.scrollIntoView({ behavior: "smooth", block: "nearest" }) |
676 | | - |
677 | | - // Wait for scroll to complete before updating button position |
678 | | - collapseTimeout2Ref.current = setTimeout(() => { |
679 | | - // updateCodeBlockButtonPosition itself should also check for refs if needed |
680 | | - updateCodeBlockButtonPosition() |
681 | | - collapseTimeout2Ref.current = null |
682 | | - }, 50) |
683 | | - } |
| 678 | + updateCodeBlockButtonPosition() |
684 | 679 | collapseTimeout1Ref.current = null |
685 | 680 | }, |
686 | 681 | WINDOW_SHADE_SETTINGS.transitionDelayS * 1000 + 50, |
|
0 commit comments