1 Commits

3 changed files with 25 additions and 13 deletions

View File

@@ -204,19 +204,22 @@ export function AIChatPanel({
{/* Scrollable messages area */}
<div className="relative flex-1 min-h-0">
{/* Gradual blur at the top of messages */}
{/* Gradual blur at the bottom of messages */}
{hasMessages && (
<GradualBlur
position="top"
strength={2}
height="5rem"
divCount={6}
position="bottom"
strength={0.6}
height="4rem"
divCount={10}
curve="ease-out"
opacity={0.8}
zIndex={20}
/>
)}
<ScrollArea
className="h-full"
viewportRef={messagesContainerRef}
scrollbarClassName={hasMessages ? 'z-30' : undefined}
viewportClassName={
isHomePage && !hasMessages
? '[&>div]:!flex [&>div]:!flex-col [&>div]:!min-h-full [&>div]:!justify-center'
@@ -382,10 +385,9 @@ export function AIChatPanel({
</ScrollArea>
</div>
{/* Fixed input — pinned to the bottom (hidden on initial state) */}
{/* Fixed input — pinned to the bottom, above the blur */}
{hasMessages && (
<div className="absolute bottom-0 left-0 right-0 z-10 px-6 pb-5 pt-16 pointer-events-none">
<div className="absolute inset-x-0 top-0 h-full bg-gradient-to-b from-transparent via-background/60 to-background/90" />
<div className="absolute bottom-0 left-0 right-0 z-30 px-6 pb-5 pt-4 pointer-events-none">
<div className="relative pointer-events-auto mx-auto max-w-3xl">
<ChatInput
input={input}

View File

@@ -13,6 +13,8 @@ interface GradualBlurProps {
divCount?: number;
/** Use exponential progression for stronger end blur */
exponential?: boolean;
/** Distribution curve: linear | ease-out */
curve?: 'linear' | 'ease-out';
/** Opacity applied to each blur layer */
opacity?: number;
/** z-index for the overlay */
@@ -30,6 +32,7 @@ export function GradualBlur({
height = '6rem',
divCount = 5,
exponential = false,
curve = 'linear',
opacity = 1,
zIndex = 10,
className = '',
@@ -39,14 +42,19 @@ export function GradualBlur({
const increment = 100 / divCount;
const direction = getGradientDirection(position);
const curveFunc = curve === 'ease-out'
? (p: number) => 1 - Math.pow(1 - p, 2)
: (p: number) => p;
for (let i = 1; i <= divCount; i++) {
const progress = i / divCount;
let progress = i / divCount;
progress = curveFunc(progress);
let blurValue: number;
if (exponential) {
blurValue = Math.pow(2, progress * 4) * 0.0625 * strength;
} else {
blurValue = 0.0625 * (progress * divCount + 1) * strength;
blurValue = progress * strength;
}
const p1 = Math.round((increment * i - increment) * 10) / 10;
@@ -77,7 +85,7 @@ export function GradualBlur({
}
return divs;
}, [position, strength, divCount, exponential, opacity]);
}, [position, strength, divCount, exponential, curve, opacity]);
return (
<div

View File

@@ -8,10 +8,12 @@ function ScrollArea({
children,
viewportRef,
viewportClassName,
scrollbarClassName,
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root> & {
viewportRef?: React.Ref<HTMLDivElement>;
viewportClassName?: string;
scrollbarClassName?: string;
}) {
return (
<ScrollAreaPrimitive.Root
@@ -29,7 +31,7 @@ function ScrollArea({
>
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollBar className={scrollbarClassName} />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
)
@@ -45,7 +47,7 @@ function ScrollBar({
data-slot="scroll-area-scrollbar"
orientation={orientation}
className={cn(
"flex touch-none p-px transition-colors select-none",
"flex touch-none p-px transition-colors select-none z-50",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent",
orientation === "horizontal" &&