Skip to content

Commit

Permalink
Better implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
cte committed Feb 28, 2025
1 parent 208f564 commit bacbd57
Showing 1 changed file with 32 additions and 41 deletions.
73 changes: 32 additions & 41 deletions webview-ui/src/components/chat/ReasoningBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { CaretSortIcon, CaretUpIcon, CounterClockwiseClockIcon } from "@radix-ui/react-icons"
import { useCallback, useEffect, useRef, useState } from "react"
import { CaretDownIcon, CaretUpIcon, CounterClockwiseClockIcon } from "@radix-ui/react-icons"

import MarkdownBlock from "../common/MarkdownBlock"
import { useMount } from "react-use"

interface ReasoningBlockProps {
content: string
Expand All @@ -13,10 +14,11 @@ interface ReasoningBlockProps {
export const ReasoningBlock = ({ content, elapsed, isCollapsed = false, onToggleCollapse }: ReasoningBlockProps) => {
const contentRef = useRef<HTMLDivElement>(null)
const elapsedRef = useRef<number>(0)
const [prevThought, setPrevThought] = useState<string>("")
const [thought, setThought] = useState<string>()
const [prevThought, setPrevThought] = useState<string>("Thinking")
const [isTransitioning, setIsTransitioning] = useState<boolean>(false)
const transitionQueueRef = useRef<string[]>([])
const isProcessingRef = useRef<boolean>(false)
const cursorRef = useRef<number>(0)
const queueRef = useRef<string[]>([])

useEffect(() => {
if (contentRef.current && !isCollapsed) {
Expand All @@ -30,70 +32,59 @@ export const ReasoningBlock = ({ content, elapsed, isCollapsed = false, onToggle
}
}, [elapsed])

// If there's only one element or the last element is empty, get the
// second-to-last element. Otherwise, return "Thinking" as a fallback.
const thought = useMemo(() => {
const sentences = String(content)
.replace(/\n/g, " ")
.split(/(?<!\d)\.\s+/) // Split on periods not preceded by digits.
return sentences.length > 1 ? sentences[sentences.length - 2]?.trim() : "Thinking"
}, [content])

// Process the transition queue.
const processNextTransition = useCallback(() => {
if (transitionQueueRef.current.length === 0) {
isProcessingRef.current = false
return
}
const nextThought = queueRef.current.pop()
queueRef.current = []

isProcessingRef.current = true
setIsTransitioning(true)
if (nextThought) {
setIsTransitioning(true)
}

setTimeout(() => {
const nextThought = transitionQueueRef.current.shift()

if (nextThought) {
setPrevThought(nextThought)
setIsTransitioning(false)
}

setIsTransitioning(false)
setTimeout(() => processNextTransition(), 200)
setTimeout(() => processNextTransition(), 500)
}, 200)
}, [])

useMount(() => {
processNextTransition()
})

useEffect(() => {
if (thought !== prevThought) {
// Add the new thought to the queue.
if (!transitionQueueRef.current.includes(thought)) {
transitionQueueRef.current.push(thought)
if (content.length - cursorRef.current > 160) {
setThought("... " + content.slice(cursorRef.current))
cursorRef.current = content.length
}
}, [content])

// Start processing if not already processing.
if (!isProcessingRef.current) {
processNextTransition()
}
}
} else if (prevThought === "") {
setPrevThought(thought)
useEffect(() => {
if (thought && thought !== prevThought) {
queueRef.current.push(thought)
}
}, [thought, prevThought, processNextTransition])
}, [thought, prevThought])

return (
<div className="bg-vscode-editor-background border border-vscode-border rounded-xs overflow-hidden">
<div
className="flex items-center justify-between gap-2 px-3 py-2 cursor-pointer"
className="flex items-center justify-between gap-1 px-3 py-2 cursor-pointer text-muted-foreground"
onClick={onToggleCollapse}>
<div
className={`truncate flex-1 text-muted-foreground transition-opacity duration-200 ${isTransitioning ? "opacity-0" : "opacity-100"}`}>
className={`truncate flex-1 transition-opacity duration-200 ${isTransitioning ? "opacity-0" : "opacity-100"}`}>
{prevThought}
</div>
<div className="flex flex-row gap-1">
<div className="flex flex-row items-center gap-1">
{elapsedRef.current > 1000 && (
<>
<CounterClockwiseClockIcon />
<CounterClockwiseClockIcon className="scale-80" />
<div>{Math.round(elapsedRef.current / 1000)}s</div>
</>
)}
{isCollapsed ? <CaretSortIcon /> : <CaretUpIcon />}
{isCollapsed ? <CaretDownIcon /> : <CaretUpIcon />}
</div>
</div>
{!isCollapsed && (
Expand Down

0 comments on commit bacbd57

Please sign in to comment.