From 71e993ad8fa66832cd57654efc2b2b9741e93c1a Mon Sep 17 00:00:00 2001 From: spencrmartin Date: Wed, 12 Nov 2025 11:13:38 -0500 Subject: [PATCH] Make sidecar invoker dock hidden by default and show only on ChatInput hover - Added hover state tracking to ChatInput component - SidecarInvoker now receives isVisible prop based on ChatInput hover state - Component uses smooth CSS transitions (opacity and translate) instead of conditional rendering - Dock slides up from below when hovering over chat input area - Maintains all existing functionality while providing better UX - Dock is contextually hidden until user needs it --- ui/desktop/src/components/ChatInput.tsx | 7 ++++++- ui/desktop/src/components/Layout/SidecarInvoker.tsx | 9 ++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx index 88cab78ea7..8d8f25edf6 100644 --- a/ui/desktop/src/components/ChatInput.tsx +++ b/ui/desktop/src/components/ChatInput.tsx @@ -349,6 +349,9 @@ export default function ChatInput({ const [isAddCommandModalOpen, setIsAddCommandModalOpen] = useState(false); const [customCommands, setCustomCommands] = useState([]); + // Hover state for sidecar dock + const [isHoveringChatInput, setIsHoveringChatInput] = useState(false); + // Sidecar functionality const sidecar = useSidecar(); @@ -1590,6 +1593,8 @@ export default function ChatInput({ data-drop-zone="true" onDrop={handleLocalDrop} onDragOver={handleLocalDragOver} + onMouseEnter={() => setIsHoveringChatInput(true)} + onMouseLeave={() => setIsHoveringChatInput(false)} > {/* Message Queue Display */} {queuedMessages.length > 0 && ( @@ -1612,7 +1617,7 @@ export default function ChatInput({ onShowLocalhost={handleShowLocalhost} onShowFileViewer={handleShowFileViewer} onAddContainer={handleAddContainer} - isVisible={true} + isVisible={isHoveringChatInput} /> {/* Input row with inline action buttons wrapped in form */} diff --git a/ui/desktop/src/components/Layout/SidecarInvoker.tsx b/ui/desktop/src/components/Layout/SidecarInvoker.tsx index c6a40276db..8bcbce6834 100644 --- a/ui/desktop/src/components/Layout/SidecarInvoker.tsx +++ b/ui/desktop/src/components/Layout/SidecarInvoker.tsx @@ -36,8 +36,7 @@ export const SidecarInvoker: React.FC = ({ } }, [isHovering]); - // NOW we can do conditional rendering after all hooks are called - if (!isVisible) return null; + // Component is always rendered but hidden/shown with CSS transitions const handleLocalhostClick = () => { console.log('🔍 SidecarInvoker: Localhost button clicked'); @@ -192,7 +191,11 @@ export const SidecarInvoker: React.FC = ({ return (
{/* Screenshot backdrops - positioned behind the dock */} {iframeBackdrops.map((backdrop) => (