feat: Discord-style timestamps, reply avatars, virtual scroll fixes

- Use formatMessageTimestamp for "Today at H:MM AM/PM" in headers
- Add mini avatar to reply references
- Fix virtual scroll height estimates for new spacing (61/22px)
- Include CSS margins in height measurement via getComputedStyle
- Fix 0px → 0 in --message-inline-spacing token
This commit is contained in:
jevb
2026-03-22 09:00:48 +01:00
parent 62e5eebd7a
commit c5d867c9da
3 changed files with 15 additions and 5 deletions
@@ -67,7 +67,9 @@ type VirtualItem = VirtualItemMessage | VirtualItemDivider;
function estimateItemHeight(item: VirtualItem): number {
if (item.kind === "divider") return 32;
let height = item.isGrouped ? 42 : 72;
// Non-grouped: min-height 2.75rem (44px @16px root) + margin-top 17px = 61px
// Grouped: min-height 1.375rem (22px @16px root) + margin-top 0px = 22px
let height = item.isGrouped ? 22 : 61;
// Image attachments
for (const att of item.message.attachments) {
@@ -235,7 +237,8 @@ export function createMessageList(options: MessageListOptions): MessageListCompo
const globalIdx = renderedStart + i;
if (globalIdx < 0 || (tree !== null && globalIdx >= tree.size)) continue;
const el = children[i] as HTMLElement;
const h = el.offsetHeight;
const style = getComputedStyle(el);
const h = el.offsetHeight + parseFloat(style.marginTop) + parseFloat(style.marginBottom);
if (h > 0) {
const key = itemKey(globalIdx);
heightCache.set(key, h);
@@ -20,6 +20,7 @@ export {
parseTimestamp,
formatTime,
formatFullDate,
formatMessageTimestamp,
isSameDay,
shouldGroup,
getUserRole,
@@ -70,7 +71,7 @@ export { renderReactions } from "./reactions";
// -- Imports for composite functions ------------------------------------------
import { formatTime, formatFullDate } from "./formatting";
import { formatTime, formatFullDate, formatMessageTimestamp } from "./formatting";
import { getUserRole, roleColorVar } from "./formatting";
import { renderMentions, renderMessageContent } from "./content-parser";
import { renderUrlEmbeds } from "./media";
@@ -98,8 +99,14 @@ function renderReplyRef(
const bar = createElement("div", { class: "msg-reply-ref" });
if (ref) {
const preview = ref.deleted ? "[message deleted]" : ref.content.slice(0, 100);
const role = getUserRole(ref.user.id);
const miniAvatar = createElement("div", {
class: "rr-avatar",
style: `background: ${roleColorVar(role)}`,
}, ref.user.username.charAt(0).toUpperCase());
appendChildren(
bar,
miniAvatar,
createElement("span", { class: "rr-author" }, ref.user.username),
createElement("span", { class: "rr-text" }, preview),
);
@@ -161,7 +168,7 @@ export function renderMessage(
class: "msg-author",
style: `color: ${roleColorVar(role)}`,
}, msg.user.username);
const time = createElement("span", { class: "msg-time", title: formatFullDate(msg.timestamp) }, formatTime(msg.timestamp));
const time = createElement("span", { class: "msg-time", title: formatFullDate(msg.timestamp) }, formatMessageTimestamp(msg.timestamp));
appendChildren(header, author, time);
el.appendChild(header);
+1 -1
View File
@@ -106,7 +106,7 @@
/* Message layout */
--message-group-spacing: 17px;
--message-inline-spacing: 0px;
--message-inline-spacing: 0;
--avatar-size: 40px;
--avatar-offset-left: 16px;
--message-content-left: 72px;