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
This commit is contained in:
spencrmartin
2025-11-12 11:13:38 -05:00
parent 5006def228
commit 71e993ad8f
2 changed files with 12 additions and 4 deletions
+6 -1
View File
@@ -349,6 +349,9 @@ export default function ChatInput({
const [isAddCommandModalOpen, setIsAddCommandModalOpen] = useState(false);
const [customCommands, setCustomCommands] = useState<CustomCommand[]>([]);
// 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 */}
@@ -36,8 +36,7 @@ export const SidecarInvoker: React.FC<SidecarInvokerProps> = ({
}
}, [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<SidecarInvokerProps> = ({
return (
<div
ref={containerRef}
className="relative w-full mb-2"
className={`relative w-full mb-2 transition-all duration-300 ease-out ${
isVisible
? 'opacity-100 translate-y-0'
: 'opacity-0 -translate-y-2 pointer-events-none'
}`}
>
{/* Screenshot backdrops - positioned behind the dock */}
{iframeBackdrops.map((backdrop) => (