update chatbox

This commit is contained in:
2026-08-08 00:39:16 +05:30
parent 90f14a73b2
commit 0cae250049
15 changed files with 1952 additions and 913 deletions

View File

@@ -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>
)}