Spaces:
Sleeping
Sleeping
| /** | |
| * ThinkingBlock — displays model thinking/reasoning blocks in the chat. | |
| * Matches original claw-code thinkback feature. | |
| */ | |
| import { useState } from "react"; | |
| import { Brain, ChevronDown, ChevronRight, Clock } from "lucide-react"; | |
| interface ThinkingBlockProps { | |
| thinking: string; | |
| durationMs?: number; | |
| collapsed?: boolean; | |
| } | |
| export function ThinkingBlock({ thinking, durationMs, collapsed: initialCollapsed = true }: ThinkingBlockProps) { | |
| const [collapsed, setCollapsed] = useState(initialCollapsed); | |
| if (!thinking) return null; | |
| const lines = thinking.split("\n"); | |
| const preview = lines.slice(0, 3).join("\n"); | |
| const hasMore = lines.length > 3 || thinking.length > 300; | |
| return ( | |
| <div className="my-2 rounded-lg border border-purple-500/20 bg-purple-500/5 overflow-hidden"> | |
| <button | |
| onClick={() => setCollapsed(!collapsed)} | |
| className="w-full flex items-center gap-2 px-3 py-2 text-sm text-purple-400 hover:bg-purple-500/10 transition-colors" | |
| > | |
| <Brain className="w-4 h-4 shrink-0" /> | |
| <span className="font-medium">Thinking</span> | |
| {durationMs && ( | |
| <span className="flex items-center gap-1 text-xs text-muted-foreground ml-auto mr-2"> | |
| <Clock className="w-3 h-3" /> | |
| {(durationMs / 1000).toFixed(1)}s | |
| </span> | |
| )} | |
| {collapsed ? ( | |
| <ChevronRight className="w-4 h-4 shrink-0 ml-auto" /> | |
| ) : ( | |
| <ChevronDown className="w-4 h-4 shrink-0 ml-auto" /> | |
| )} | |
| </button> | |
| {!collapsed && ( | |
| <div className="px-3 pb-3 text-sm text-muted-foreground font-mono whitespace-pre-wrap border-t border-purple-500/10 pt-2 max-h-96 overflow-y-auto"> | |
| {thinking} | |
| </div> | |
| )} | |
| {collapsed && hasMore && ( | |
| <div className="px-3 pb-2 text-xs text-muted-foreground font-mono whitespace-pre-wrap opacity-60 line-clamp-2"> | |
| {preview.substring(0, 200)}{preview.length >= 200 ? "..." : ""} | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |
| /** | |
| * Parse thinking blocks from a message content array. | |
| */ | |
| export function extractThinkingBlocks(content: unknown): { type: "thinking"; thinking: string; durationMs?: number }[] { | |
| if (!Array.isArray(content)) return []; | |
| return content.filter( | |
| (block: any) => block.type === "thinking" && block.thinking | |
| ) as { type: "thinking"; thinking: string; durationMs?: number }[]; | |
| } | |