fix: keep sticky code block headers at the top of channel scroll containers (#29836)

* fix: keep sticky code block headers at the top of channel scroll containers

* fix: clip the sticky code block header to the block's rounded corners

* fix: give channel scroll containers their own layer so firefox clips sticky code headers
This commit is contained in:
G30 2026-09-09 12:12:15 -04:00 committed by GitHub
parent babf08e301
commit b38755b21c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 94 additions and 87 deletions

View file

@ -94,62 +94,64 @@
</div>
{:else}
<div
class="flex flex-col gap-2 max-h-[60vh] overflow-y-auto scrollbar-thin scrollbar-thumb-gray-300 dark:scrollbar-thumb-gray-700 scrollbar-track-transparent pt-7 pb-2"
class="max-h-[60vh] overflow-y-auto will-change-transform scrollbar-thin scrollbar-thumb-gray-300 dark:scrollbar-thumb-gray-700 scrollbar-track-transparent pb-2"
>
{#if pinnedMessages.length === 0}
<div class=" text-center text-xs text-gray-500 dark:text-gray-400 py-6">
{$i18n.t('No pinned messages')}
</div>
{:else}
{#each pinnedMessages as message, messageIdx (message.id)}
<Message
id="pinned"
className="rounded-xl px-2"
{message}
{channel}
onPin={async (message) => {
pinnedMessages = pinnedMessages.filter((m) => m.id !== message.id);
onPin(message.id, !message.is_pinned);
<div class="flex flex-col gap-2 pt-7">
{#if pinnedMessages.length === 0}
<div class=" text-center text-xs text-gray-500 dark:text-gray-400 py-6">
{$i18n.t('No pinned messages')}
</div>
{:else}
{#each pinnedMessages as message, messageIdx (message.id)}
<Message
id="pinned"
className="rounded-xl px-2"
{message}
{channel}
onPin={async (message) => {
pinnedMessages = pinnedMessages.filter((m) => m.id !== message.id);
onPin(message.id, !message.is_pinned);
const updatedMessage = await pinMessage(
localStorage.token,
message.channel_id,
message.id,
!message.is_pinned
).catch((error) => {
toast.error(`${error}`);
return null;
});
const updatedMessage = await pinMessage(
localStorage.token,
message.channel_id,
message.id,
!message.is_pinned
).catch((error) => {
toast.error(`${error}`);
return null;
});
init();
}}
onReaction={false}
onThread={false}
onReply={false}
onEdit={false}
onDelete={false}
/>
{#if messageIdx === pinnedMessages.length - 1 && !allItemsLoaded}
<Loader
on:visible={(e) => {
console.log('visible');
if (!loading) {
page += 1;
getPinnedMessages();
}
init();
}}
>
<div
class="w-full flex justify-center py-1 text-xs animate-pulse items-center gap-2"
onReaction={false}
onThread={false}
onReply={false}
onEdit={false}
onDelete={false}
/>
{#if messageIdx === pinnedMessages.length - 1 && !allItemsLoaded}
<Loader
on:visible={(e) => {
console.log('visible');
if (!loading) {
page += 1;
getPinnedMessages();
}
}}
>
<Spinner className=" size-4" />
<div class=" ">{$i18n.t('Loading...')}</div>
</div>
</Loader>
{/if}
{/each}
{/if}
<div
class="w-full flex justify-center py-1 text-xs animate-pulse items-center gap-2"
>
<Spinner className=" size-4" />
<div class=" ">{$i18n.t('Loading...')}</div>
</div>
</Loader>
{/if}
{/each}
{/if}
</div>
</div>
{/if}
</div>

View file

@ -188,43 +188,48 @@
</div>
</div>
<div class="flex-1 min-h-0 w-full overflow-y-auto pt-7" bind:this={messagesContainerElement}>
{#if messages !== null}
<Messages
id={threadId}
{channel}
{top}
{messages}
{replyToMessage}
thread={true}
{onPin}
onReply={async (message) => {
replyToMessage = message;
<div
class="flex-1 min-h-0 w-full overflow-y-auto will-change-transform"
bind:this={messagesContainerElement}
>
<div class="pt-7">
{#if messages !== null}
<Messages
id={threadId}
{channel}
{top}
{messages}
{replyToMessage}
thread={true}
{onPin}
onReply={async (message) => {
replyToMessage = message;
await tick();
chatInputElement?.focus();
}}
onLoad={async () => {
const newMessages = await getChannelThreadMessages(
localStorage.token,
channel.id,
threadId,
messages.length
);
await tick();
chatInputElement?.focus();
}}
onLoad={async () => {
const newMessages = await getChannelThreadMessages(
localStorage.token,
channel.id,
threadId,
messages.length
);
messages = [...messages, ...newMessages];
messages = [...messages, ...newMessages];
if (newMessages.length < 50) {
top = true;
return;
}
}}
/>
{:else}
<div class="w-full flex justify-center pt-5 pb-10">
<Spinner />
</div>
{/if}
if (newMessages.length < 50) {
top = true;
return;
}
}}
/>
{:else}
<div class="w-full flex justify-center pt-5 pb-10">
<Spinner />
</div>
{/if}
</div>
</div>
<div class=" pb-[1rem] px-2.5 w-full">

View file

@ -433,7 +433,7 @@
<div>
<div
class="relative {className} flex flex-col rounded-2xl border border-gray-100/30 dark:border-gray-850/30 my-0.5"
class="relative {className} flex flex-col rounded-2xl border border-gray-100/30 dark:border-gray-850/30 my-0.5 overflow-clip"
dir="ltr"
>
{#if ['mermaid', 'vega', 'vega-lite'].includes(lang)}
@ -457,7 +457,7 @@
{/if}
{:else}
<div
class="sticky {stickyButtonsClassName} left-0 right-0 py-1.5 px-3.5 gap-2 flex items-center justify-end w-full z-10 text-xs text-black dark:text-white bg-white dark:bg-black rounded-t-2xl"
class="sticky {stickyButtonsClassName} left-0 right-0 py-1.5 px-3.5 gap-2 flex items-center justify-end w-full z-10 text-xs text-black dark:text-white bg-white dark:bg-black"
>
<div class="flex-1 truncate">
<Tooltip content={lang} placement="top-start">