update chatbox
This commit is contained in:
@@ -4,9 +4,27 @@ import { cn } from '@/lib/utils';
|
||||
import KrowAssistant from './KrowAssistant';
|
||||
import { useAssistantPanel } from './AssistantPanelContext';
|
||||
|
||||
/** Panel width, plus the gutter separating it from the dashboard. */
|
||||
const PANEL_WIDTH = 380;
|
||||
const DEFAULT_WIDTH = 380;
|
||||
const EXPANDED_WIDTH = 620;
|
||||
/** Gutter separating the panel from the dashboard. */
|
||||
const GUTTER = 24;
|
||||
/** Expanded must never dominate: the dashboard stays the primary experience. */
|
||||
const MAX_VIEWPORT_SHARE = 0.42;
|
||||
|
||||
/** Tracks viewport width so the expanded panel can be clamped on smaller screens. */
|
||||
function useViewportWidth() {
|
||||
const [width, setWidth] = React.useState(() =>
|
||||
typeof window === 'undefined' ? 1440 : window.innerWidth
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const onResize = () => setWidth(window.innerWidth);
|
||||
window.addEventListener('resize', onResize);
|
||||
return () => window.removeEventListener('resize', onResize);
|
||||
}, []);
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* AssistantPanel — the sliding column the layout renders.
|
||||
@@ -15,23 +33,30 @@ const GUTTER = 24;
|
||||
* dashboard reflow smoothly instead of being covered, and what lets closing
|
||||
* restore the original layout exactly.
|
||||
*
|
||||
* Two things about the structure matter, and both were bugs before:
|
||||
* Two structural details matter, and both were bugs at one point:
|
||||
*
|
||||
* 1. The clip lives *inside* the sticky element, not around it. An ancestor with
|
||||
* `overflow: hidden` becomes the nearest scroll container, which silently
|
||||
* disables `position: sticky` on everything beneath it — the panel computed
|
||||
* as sticky but never actually stuck.
|
||||
* 1. The clip lives *inside* the sticky element. An ancestor with
|
||||
* `overflow: hidden` becomes the nearest scroll container and silently
|
||||
* disables `position: sticky` beneath it.
|
||||
* 2. The aside stretches to the shell's full height (`self-stretch`), so its
|
||||
* sticky context runs the whole page rather than ending partway down and
|
||||
* leaving empty space beside a long dashboard.
|
||||
*
|
||||
* 2. The aside must stretch to the shell's full height. As a flex item under
|
||||
* `items-start` it collapsed to its own height, so its sticky context ended
|
||||
* partway down the page and the panel scrolled away, leaving empty space
|
||||
* beside a long dashboard.
|
||||
*
|
||||
* With both fixed the panel stays pinned for the whole scroll, and because the
|
||||
* assistant is a fixed-height frame, only its conversation scrolls.
|
||||
* Expanding widens the same panel in place — it is an analysis workspace inside
|
||||
* the application layout, not a fullscreen takeover, and never covers the
|
||||
* dashboard.
|
||||
*/
|
||||
export function AssistantPanel({ stickyClassName, panelHeightClassName }) {
|
||||
const { context, isOpen, close } = useAssistantPanel();
|
||||
const { context, isOpen, isExpanded, close, expand, restore } = useAssistantPanel();
|
||||
const viewportWidth = useViewportWidth();
|
||||
|
||||
// Clamped so a wide panel cannot squeeze the dashboard on a small screen.
|
||||
const panelWidth = isExpanded
|
||||
? Math.max(DEFAULT_WIDTH, Math.min(EXPANDED_WIDTH, Math.round(viewportWidth * MAX_VIEWPORT_SHARE)))
|
||||
: DEFAULT_WIDTH;
|
||||
|
||||
const trackWidth = panelWidth + GUTTER;
|
||||
const easing = [0.65, 0, 0.35, 1];
|
||||
|
||||
return (
|
||||
<AnimatePresence initial={false}>
|
||||
@@ -40,34 +65,40 @@ export function AssistantPanel({ stickyClassName, panelHeightClassName }) {
|
||||
key="assistant"
|
||||
aria-label="Krow AI Assistant"
|
||||
initial={{ width: 0, opacity: 0 }}
|
||||
animate={{ width: PANEL_WIDTH + GUTTER, opacity: 1 }}
|
||||
animate={{ width: trackWidth, opacity: 1 }}
|
||||
exit={{ width: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.3, ease: [0.65, 0, 0.35, 1] }}
|
||||
/* No `overflow-hidden` here — see (1) above. `self-stretch` keeps the
|
||||
sticky context running the full height of the shell — see (2). */
|
||||
transition={{ duration: 0.3, ease: easing }}
|
||||
/* No `overflow-hidden` here — see (1). `self-stretch` — see (2). */
|
||||
className="relative shrink-0 self-stretch"
|
||||
>
|
||||
<div className={cn('sticky', stickyClassName)}>
|
||||
{/* The clip: contains the slide-in and the fixed panel width while
|
||||
the track animates open, without breaking the sticky above it. */}
|
||||
<div className="overflow-hidden" style={{ width: PANEL_WIDTH + GUTTER }}>
|
||||
{/* The clip: contains the slide-in and the panel width while the
|
||||
track animates, without breaking the sticky above it. */}
|
||||
<motion.div
|
||||
className="overflow-hidden"
|
||||
animate={{ width: trackWidth }}
|
||||
transition={{ duration: 0.3, ease: easing }}
|
||||
>
|
||||
<motion.div
|
||||
className="pl-6"
|
||||
initial={{ x: 32, opacity: 0 }}
|
||||
animate={{ x: 0, opacity: 1 }}
|
||||
exit={{ x: 32, opacity: 0 }}
|
||||
transition={{ duration: 0.3, ease: [0.65, 0, 0.35, 1] }}
|
||||
transition={{ duration: 0.3, ease: easing }}
|
||||
>
|
||||
{/* Keyed on the context so switching pages gives a clean panel
|
||||
and aborts any response still streaming for the last one. */}
|
||||
<KrowAssistant
|
||||
key={context.id}
|
||||
context={context}
|
||||
expanded={isExpanded}
|
||||
onClose={close}
|
||||
onExpand={expand}
|
||||
onRestore={restore}
|
||||
className={panelHeightClassName}
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.aside>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user