From 414e2ccae54564d906882702d01253fd018938df Mon Sep 17 00:00:00 2001 From: Nicolas Varrot Date: Sun, 15 Feb 2026 20:39:28 +0000 Subject: [PATCH] fix: session tooltip click-through, copy buttons stopPropagation, rename spacebar (v1.64.2) --- src/components/CodeBlock.tsx | 20 +++++++++++--------- src/components/Header.tsx | 19 +++++++++++++------ src/components/Sidebar.tsx | 1 + src/components/ToolCall.tsx | 4 +++- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/components/CodeBlock.tsx b/src/components/CodeBlock.tsx index e131f5b..4e7f91a 100644 --- a/src/components/CodeBlock.tsx +++ b/src/components/CodeBlock.tsx @@ -67,15 +67,17 @@ export function CodeBlock(props: HTMLAttributes) { const hasEnoughLines = lines.length > LINE_THRESHOLD; const isCollapsible = lines.length > COLLAPSE_THRESHOLD; - const handleCopy = useCallback(() => { - if (typeof code === 'string') { - copyToClipboard(code).then((ok) => { - if (ok) { - setCopied(true); - setTimeout(() => setCopied(false), 2000); - } - }); - } + const handleCopy = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + const text = typeof code === 'string' ? code : ''; + if (!text) return; + copyToClipboard(text).then((ok) => { + if (ok) { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } + }); }, [code]); const toggleLineNumbers = useCallback(() => { diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 8aae09f..29ed704 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState, useRef, useEffect } from 'react'; +import { useCallback, useState, useRef, useEffect, forwardRef } from 'react'; import { Menu, Sparkles, LogOut, Cpu, Bot, Download, Minimize2, Info, Copy, Check, Settings } from 'lucide-react'; import type { ConnectionStatus, Session, ChatMessage } from '../types'; import { useT } from '../hooks/useLocale'; @@ -28,11 +28,17 @@ export function Header({ status, sessionKey, onToggleSidebar, activeSessionData, const [settingsOpen, setSettingsOpen] = useState(false); const sessionInfoRef = useRef(null); - // Close popover on outside click + const popoverRef = useRef(null); + + // Close popover on outside click — check both the trigger area AND the popover itself useEffect(() => { if (!showSessionInfo) return; const handler = (e: MouseEvent) => { - if (sessionInfoRef.current && !sessionInfoRef.current.contains(e.target as Node)) { + const target = e.target as Node; + if ( + sessionInfoRef.current && !sessionInfoRef.current.contains(target) && + popoverRef.current && !popoverRef.current.contains(target) + ) { setShowSessionInfo(false); } }; @@ -74,7 +80,7 @@ export function Header({ status, sessionKey, onToggleSidebar, activeSessionData, {showSessionInfo && activeSessionData && ( - setShowSessionInfo(false)} /> + setShowSessionInfo(false)} /> )}
@@ -169,7 +175,7 @@ function CopyField({ value }: { value: string }) { ); } -function SessionInfoPopover({ session, sessionKey, messageCount, onClose }: { session: Session; sessionKey: string; messageCount: number; onClose: () => void }) { +const SessionInfoPopover = forwardRef void }>(function SessionInfoPopover({ session, sessionKey, messageCount, onClose }, ref) { const t = useT(); const rows: Array<{ label: string; value: string; copyable?: boolean }> = [ { label: t('sessionInfo.sessionKey'), value: sessionKey, copyable: true }, @@ -191,6 +197,7 @@ function SessionInfoPopover({ session, sessionKey, messageCount, onClose }: { se return (
); -} +}); function CompactButton({ sessionKey, onCompact }: { sessionKey: string; onCompact: (key: string) => Promise }) { const [compacting, setCompacting] = useState(false); diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index a44d792..2ae5f3f 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -543,6 +543,7 @@ export function Sidebar({ sessions, activeSession, onSwitch, onDelete, onSplit, onChange={(e) => setRenameValue(e.target.value)} onBlur={commitRename} onKeyDown={(e) => { + e.stopPropagation(); if (e.key === 'Enter') { e.preventDefault(); commitRename(); } if (e.key === 'Escape') { e.preventDefault(); cancelRename(); } }} diff --git a/src/components/ToolCall.tsx b/src/components/ToolCall.tsx index 6e78405..e3dcc9d 100644 --- a/src/components/ToolCall.tsx +++ b/src/components/ToolCall.tsx @@ -153,7 +153,9 @@ function WrapToggle({ wrap, onToggle }: { wrap: boolean; onToggle: () => void }) /** Small copy-to-clipboard button for tool call content blocks. */ function CopyButton({ text }: { text: string }) { const [copied, setCopied] = useState(false); - const handleCopy = useCallback(() => { + const handleCopy = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); copyToClipboard(text).then((ok) => { if (ok) { setCopied(true);