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}
|
ref={messageRegionRef}
|
||||||
data-testid="thread-message-region"
|
data-testid="thread-message-region"
|
||||||
className={cn(
|
className={cn(
|
||||||
"thread-viewport-scrollbar row-start-1 flex min-h-0 min-w-0 flex-col",
|
"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-4 pt-4 sm:px-4",
|
"scroll-auto justify-start overflow-x-hidden px-3 pb-0 pt-4 sm:px-4",
|
||||||
"[overflow-anchor:none] [scrollbar-width:none]",
|
"[overflow-anchor:none] [scrollbar-width:none]",
|
||||||
"[&::-webkit-scrollbar]:hidden",
|
"[&::-webkit-scrollbar]:hidden",
|
||||||
hasVerticalOverflow ? "overflow-y-auto" : "overflow-hidden",
|
hasVerticalOverflow ? "overflow-y-auto" : "overflow-hidden",
|
||||||
@@ -768,6 +768,7 @@ export const ThreadViewport = forwardRef<ThreadViewportHandle, ThreadViewportPro
|
|||||||
onQuoteSelection={onQuoteSelection}
|
onQuoteSelection={onQuoteSelection}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div aria-hidden className="thread-message-end-gap shrink-0" />
|
||||||
<div ref={bottomRef} aria-hidden className="h-px shrink-0" />
|
<div ref={bottomRef} aria-hidden className="h-px shrink-0" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -808,7 +809,7 @@ export const ThreadViewport = forwardRef<ThreadViewportHandle, ThreadViewportPro
|
|||||||
}}
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
"row-start-2 z-10 w-full",
|
"row-start-2 z-10 w-full",
|
||||||
hasMessages ? "relative bg-background" : "relative self-center",
|
hasMessages ? "thread-composer-dock relative" : "relative self-center",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -377,6 +377,7 @@
|
|||||||
* bottom without a remount or a transform clone.
|
* bottom without a remount or a transform clone.
|
||||||
*/
|
*/
|
||||||
.thread-layout {
|
.thread-layout {
|
||||||
|
--thread-composer-fade-height: 2.25rem;
|
||||||
grid-template-rows: minmax(min-content, 1fr) auto 0fr;
|
grid-template-rows: minmax(min-content, 1fr) auto 0fr;
|
||||||
transition: grid-template-rows 220ms ease-out;
|
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 {
|
@keyframes composer-status-strip-enter {
|
||||||
0% {
|
0% {
|
||||||
max-height: 0;
|
max-height: 0;
|
||||||
|
|||||||
@@ -259,7 +259,8 @@ describe("ThreadViewport", () => {
|
|||||||
const messageRegion = screen.getByTestId("thread-message-region");
|
const messageRegion = screen.getByTestId("thread-message-region");
|
||||||
expect(messageRegion).toHaveClass("justify-start");
|
expect(messageRegion).toHaveClass("justify-start");
|
||||||
expect(messageRegion).not.toHaveClass("justify-end");
|
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");
|
expect(messageRegion.className).not.toContain("5rem");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -316,7 +317,9 @@ describe("ThreadViewport", () => {
|
|||||||
expect(scroller).not.toContainElement(composerDock);
|
expect(scroller).not.toContainElement(composerDock);
|
||||||
expect(scroller.parentElement).toContainElement(composerDock);
|
expect(scroller.parentElement).toContainElement(composerDock);
|
||||||
expect(composerDock).toHaveClass("relative");
|
expect(composerDock).toHaveClass("relative");
|
||||||
|
expect(composerDock).toHaveClass("thread-composer-dock");
|
||||||
expect(composerDock).not.toHaveClass("sticky");
|
expect(composerDock).not.toHaveClass("sticky");
|
||||||
|
expect(scroller.querySelector(".thread-message-end-gap")).toBeInTheDocument();
|
||||||
expect(scroller.lastElementChild).toHaveClass("h-px", "shrink-0");
|
expect(scroller.lastElementChild).toHaveClass("h-px", "shrink-0");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user