mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site): fix sticky user message clipping and fade-in behavior (#23928)
The sticky user message in the chat timeline had two visual issues: 1. **Dead space during scroll** — the clipping calculation subtracted 48px prematurely (`fullHeight - scrolledPast - 48`), causing the message to shrink before its content had actually left the viewport. Removed the offset so clipping begins exactly when content scrolls out of view. 2. **Blur/gradient popping in abruptly** — the `--fade-opacity` variable was a binary 0/1 toggle. Now it ramps 0→1 over the last 40px before `MIN_HEIGHT`, so the blur and bottom gradient only appear when the message is fully compressed. Also added a longer (~25 line) user message to the `WithMessageHistory` story to make the sticky behavior easier to test visually.
This commit is contained in:
@@ -351,7 +351,7 @@ export const WithMessageHistory: Story = {
|
||||
},
|
||||
],
|
||||
},
|
||||
// -- Turn 3: user follow-up --
|
||||
// -- Turn 3: user follow-up (long message) --
|
||||
{
|
||||
id: 3,
|
||||
chat_id: CHAT_ID,
|
||||
@@ -360,7 +360,33 @@ export const WithMessageHistory: Story = {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Can you show me the token validation code and a comparison of the old vs new approach?",
|
||||
text: [
|
||||
"Can you show me the token validation code and a comparison of the old vs new approach?",
|
||||
"",
|
||||
"I have a lot of context I want to share so you can give me the best possible answer.",
|
||||
"The current token validation is scattered across multiple files and it is really hard",
|
||||
"to follow the flow from HTTP request to database lookup to response. The middleware in",
|
||||
"coderd/httpmw/apikey.go does way too much - it parses the token, validates the signature,",
|
||||
"checks expiration, looks up the user, checks if the user is suspended, and then sets up",
|
||||
"the context. That is at least 6 different responsibilities in a single middleware function.",
|
||||
"",
|
||||
"Here are the specific files I have been looking at:",
|
||||
"- coderd/httpmw/apikey.go (main middleware, ~400 lines)",
|
||||
"- coderd/httpmw/oauth2.go (OAuth2 token handling)",
|
||||
"- coderd/httpmw/session.go (session cookie management)",
|
||||
"- coderd/userauth.go (login/logout handlers)",
|
||||
"- coderd/apikey.go (API key CRUD operations)",
|
||||
"- enterprise/coderd/proxyhealth.go (proxy authentication)",
|
||||
"",
|
||||
"The problem is that ExtractAPIKeyMW is doing too many things at once:",
|
||||
"1. Extracting the token from the request (cookie or header)",
|
||||
"2. Splitting the token into key ID and secret",
|
||||
"3. Looking up the API key in the database",
|
||||
"4. Hashing the secret and comparing it",
|
||||
"5. Checking if the key is expired",
|
||||
"",
|
||||
"Can you incorporate all of this into your comparison of the old vs new approach?",
|
||||
].join("\n"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -602,6 +602,7 @@ const ChatMessageItem = memo<{
|
||||
<div
|
||||
className="pointer-events-none absolute inset-x-0 bottom-0 h-1/2 max-h-12"
|
||||
style={{
|
||||
opacity: "var(--fade-opacity, 0)",
|
||||
background:
|
||||
"linear-gradient(to top, hsl(var(--surface-secondary)), transparent)",
|
||||
}}
|
||||
@@ -793,15 +794,17 @@ const StickyUserMessage = memo<{
|
||||
container.style.top = "0px";
|
||||
return;
|
||||
}
|
||||
const visible = Math.max(fullHeight - scrolledPast - 48, MIN_HEIGHT);
|
||||
const visible = Math.max(fullHeight - scrolledPast, MIN_HEIGHT);
|
||||
container.style.setProperty("--clip-h", `${visible}px`);
|
||||
// Only show the fade gradient once enough content is
|
||||
// clipped to be visually meaningful.
|
||||
container.style.setProperty(
|
||||
"--fade-opacity",
|
||||
visible < fullHeight - 8 ? "1" : "0",
|
||||
// Only show the blur and gradient once the message
|
||||
// is near its minimum compressed height. Ramp over
|
||||
// the last 40px before MIN_HEIGHT so it doesn't pop.
|
||||
const FADE_RANGE = 40;
|
||||
const fade = Math.max(
|
||||
0,
|
||||
Math.min((MIN_HEIGHT + FADE_RANGE - visible) / FADE_RANGE, 1),
|
||||
);
|
||||
|
||||
container.style.setProperty("--fade-opacity", String(fade));
|
||||
// Push-up effect: when the next user message's sentinel
|
||||
// approaches the bottom of this sticky container, shift
|
||||
// this container upward so it slides out of view — the
|
||||
@@ -960,6 +963,7 @@ const StickyUserMessage = memo<{
|
||||
<div
|
||||
className="absolute inset-0 backdrop-blur-[1px] bg-surface-primary/15"
|
||||
style={{
|
||||
opacity: "var(--fade-opacity, 0)",
|
||||
maxHeight: "calc(var(--clip-h, 100%) + 48px)",
|
||||
willChange: "max-height, mask-image",
|
||||
maskImage:
|
||||
|
||||
Reference in New Issue
Block a user