mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-31 16:21:50 +03:00
fix(webui): soften composer scroll boundary
This commit is contained in:
@@ -744,8 +744,8 @@ export const ThreadViewport = forwardRef<ThreadViewportHandle, ThreadViewportPro
|
||||
ref={messageRegionRef}
|
||||
data-testid="thread-message-region"
|
||||
className={cn(
|
||||
"thread-viewport-scrollbar row-start-1 flex min-h-0 min-w-0 flex-col",
|
||||
"scroll-auto justify-start overflow-x-hidden px-3 pb-4 pt-4 sm:px-4",
|
||||
"thread-message-viewport thread-viewport-scrollbar row-start-1 flex min-h-0 min-w-0 flex-col",
|
||||
"scroll-auto justify-start overflow-x-hidden px-3 pb-0 pt-4 sm:px-4",
|
||||
"[overflow-anchor:none] [scrollbar-width:none]",
|
||||
"[&::-webkit-scrollbar]:hidden",
|
||||
hasVerticalOverflow ? "overflow-y-auto" : "overflow-hidden",
|
||||
@@ -768,6 +768,7 @@ export const ThreadViewport = forwardRef<ThreadViewportHandle, ThreadViewportPro
|
||||
onQuoteSelection={onQuoteSelection}
|
||||
/>
|
||||
</div>
|
||||
<div aria-hidden className="thread-message-end-gap shrink-0" />
|
||||
<div ref={bottomRef} aria-hidden className="h-px shrink-0" />
|
||||
</div>
|
||||
) : (
|
||||
@@ -808,7 +809,7 @@ export const ThreadViewport = forwardRef<ThreadViewportHandle, ThreadViewportPro
|
||||
}}
|
||||
className={cn(
|
||||
"row-start-2 z-10 w-full",
|
||||
hasMessages ? "relative bg-background" : "relative self-center",
|
||||
hasMessages ? "thread-composer-dock relative" : "relative self-center",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -377,6 +377,7 @@
|
||||
* bottom without a remount or a transform clone.
|
||||
*/
|
||||
.thread-layout {
|
||||
--thread-composer-fade-height: 2.25rem;
|
||||
grid-template-rows: minmax(min-content, 1fr) auto 0fr;
|
||||
transition: grid-template-rows 220ms ease-out;
|
||||
}
|
||||
@@ -392,6 +393,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the transcript and composer visually continuous. The scrollport owns
|
||||
* the fade because overflow clips its contents before an adjacent overlay
|
||||
* can soften the edge. The matching in-flow gap keeps the last line fully
|
||||
* visible once the user reaches the bottom.
|
||||
*/
|
||||
.thread-message-viewport {
|
||||
-webkit-mask-image: linear-gradient(
|
||||
to bottom,
|
||||
#000 0,
|
||||
#000 calc(100% - var(--thread-composer-fade-height)),
|
||||
transparent 100%
|
||||
);
|
||||
mask-image: linear-gradient(
|
||||
to bottom,
|
||||
#000 0,
|
||||
#000 calc(100% - var(--thread-composer-fade-height)),
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
.thread-message-end-gap {
|
||||
height: var(--thread-composer-fade-height);
|
||||
}
|
||||
.thread-composer-dock {
|
||||
background: hsl(var(--background));
|
||||
}
|
||||
|
||||
@keyframes composer-status-strip-enter {
|
||||
0% {
|
||||
max-height: 0;
|
||||
|
||||
@@ -259,7 +259,8 @@ describe("ThreadViewport", () => {
|
||||
const messageRegion = screen.getByTestId("thread-message-region");
|
||||
expect(messageRegion).toHaveClass("justify-start");
|
||||
expect(messageRegion).not.toHaveClass("justify-end");
|
||||
expect(messageRegion).toHaveClass("pb-4");
|
||||
expect(messageRegion).toHaveClass("thread-message-viewport");
|
||||
expect(messageRegion).toHaveClass("pb-0");
|
||||
expect(messageRegion.className).not.toContain("5rem");
|
||||
});
|
||||
|
||||
@@ -316,7 +317,9 @@ describe("ThreadViewport", () => {
|
||||
expect(scroller).not.toContainElement(composerDock);
|
||||
expect(scroller.parentElement).toContainElement(composerDock);
|
||||
expect(composerDock).toHaveClass("relative");
|
||||
expect(composerDock).toHaveClass("thread-composer-dock");
|
||||
expect(composerDock).not.toHaveClass("sticky");
|
||||
expect(scroller.querySelector(".thread-message-end-gap")).toBeInTheDocument();
|
||||
expect(scroller.lastElementChild).toHaveClass("h-px", "shrink-0");
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user