DocumentBackGutter.tsx2.5 KBView on GitHub 'use client';
import { ArrowLeft } from 'lucide-react';
import { useState } from 'react';
import { cn } from '@/lib/utils';
/**
* Full-height back gutter on the left edge of a document surface, mirroring the
* conversation view's ConversationBackGutter: click anywhere in the left margin
* to go up one layer. The visual (hover gradient + arrow) is shared so every
* document surface feels identical; the host positions/sizes the strip via
* `className` (sticky-float for scroll containers, absolute for fixed shells)
* so the arrow lands in the margin beside its centered content column.
*/
export function DocumentBackGutter({
onUp,
className,
}: {
onUp: () => void;
className?: string;
}) {
const [hovered, setHovered] = useState(false);
return (
<button
type="button"
onClick={onUp}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
aria-label="Back"
className={cn('z-10 cursor-pointer', className)}
>
<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',
)}
/>
<ArrowLeft
className={cn(
'absolute left-4 top-3 h-4 w-4 transition-opacity duration-200',
hovered ? 'opacity-80' : 'opacity-40',
)}
/>
</button>
);
}
/**
* The narrow-layout twin of `DocumentBackGutter`, mirroring ConversationHeader:
* once the surface is too narrow to spare a left margin, the gutter hides and this
* compact button takes over, merged into the top-left of the main panel's own
* header row. Host must declare `@container` so the swap tracks the panel width
* rather than the viewport.
*
* The default swap point (`@max-3xl`) suits the ~760px / 75ch columns — margin is
* still usable past `@max-4xl`, so collapsing there felt early. Hosts with a wider
* column run out of margin sooner, so they hide their gutter at `@max-4xl` and pass
* a matching `@max-4xl:flex` here to keep the pair in sync.
*/
export function InlineBackButton({ onUp, className }: { onUp: () => void; className?: string }) {
return (
<button
type="button"
onClick={onUp}
aria-label="Back"
className={cn(
'hidden h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-muted hover:text-foreground @max-3xl:flex',
className,
)}
>
<ArrowLeft className="h-4 w-4" />
</button>
);
}