AgentBackGutter.tsx2.4 KBView on GitHub
'use client';

import { ArrowLeft } from 'lucide-react';
import { useState } from 'react';
import { useAgentBack } from '@/modules/agents/hooks/use-agent-back';
import { cn } from '@/lib/utils';

/**
 * Full-height back gutter on the left edge of the agent workspace.
 *
 * A copy of ConversationBackGutter's geometry rather than a shared component: that
 * one pops the conversation's inner drill-in stack first (a Slack thread, a file doc),
 * which an agent has no equivalent of. Below that the two now agree — both leave through
 * `useBackOrClose`, so an artifact is left by popping the entry its open pushed. The
 * SHAPE is what has to match — same width formula, same hover wash, same arrow
 * position — so the two views feel like one product. If a third caller appears,
 * that is the moment to extract the shell and leave the back behaviour a prop.
 */
export function AgentBackGutter({ className }: { className?: string }) {
  const goBack = useAgentBack();
  const [hovered, setHovered] = useState(false);

  return (
    <button
      type="button"
      // Pops the entry the agent's own `?agentId=` push created, so back returns you to
      // whatever you opened the agent from — and never pushes, which is what made this
      // button and the one on the page behind it ping-pong. See useAgentBack.
      onClick={goBack}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
      aria-label="Back"
      className={cn(
        // Fills the left margin but stops 1rem short of the content column so neither
        // the arrow nor its hover gradient crowds the content.
        'sticky top-0 float-left z-10 h-screen w-[max(2rem,calc((100%-80ch)/2-1rem))] cursor-pointer',
        className,
      )}
      style={{ marginBottom: '-100vh' }}
    >
      <div
        className={cn(
          'absolute inset-0 bg-gradient-to-r from-[#15803d]/[0.10] to-transparent transition-opacity duration-300 dark:from-[#15803d]/[0.14]',
          hovered ? 'opacity-100' : 'opacity-0',
        )}
      />
      {/* top-4 centers the arrow on the title row — same arithmetic as the
          conversation gutter: pt-1.5 + py-1 + a 28px text-xl line box. */}
      <ArrowLeft
        className={cn(
          'absolute left-4 top-4 h-4 w-4 transition-opacity duration-200',
          hovered ? 'opacity-80' : 'opacity-40',
        )}
      />
    </button>
  );
}