diff --git a/frontend/app/chat/ChatShell.tsx b/frontend/app/chat/ChatShell.tsx
index ceaff56..89fed22 100644
--- a/frontend/app/chat/ChatShell.tsx
+++ b/frontend/app/chat/ChatShell.tsx
@@ -290,10 +290,8 @@ export default function ChatShell({
return (
diff --git a/frontend/app/chat/[id]/ChatRoomClient.tsx b/frontend/app/chat/[id]/ChatRoomClient.tsx
index 4ce2476..76127b9 100644
--- a/frontend/app/chat/[id]/ChatRoomClient.tsx
+++ b/frontend/app/chat/[id]/ChatRoomClient.tsx
@@ -382,8 +382,9 @@ export default function ChatRoomClient({
const scrollToBottom = useCallback((smooth = false) => {
const el = listRef.current;
if (!el) return;
+ // column-reverse:scrollTop=0 即视觉底部(最新消息)
el.scrollTo({
- top: el.scrollHeight,
+ top: 0,
behavior: smooth ? "smooth" : "auto",
});
}, []);
@@ -412,7 +413,7 @@ export default function ChatRoomClient({
if (!canView) return;
realtime.joinRoom(roomId);
void refreshMembers();
- requestAnimationFrame(() => scrollToBottom(false));
+ // 列表用 flex-col-reverse,SSR/无 JS 默认即贴底,无需首屏 scrollToBottom(避免 F5 闪一下)
markReadToLatest();
return () => {
realtime.leaveRoom(roomId);
@@ -431,9 +432,8 @@ export default function ChatRoomClient({
requestAnimationFrame(() => {
const el = listRef.current;
if (!el) return;
- const nearBottom =
- el.scrollHeight - el.scrollTop - el.clientHeight < 120;
- if (nearBottom) {
+ // column-reverse:靠近 scrollTop=0 即贴底
+ if (el.scrollTop < 120) {
scrollToBottom(true);
markReadToLatest();
}
@@ -499,7 +499,9 @@ export default function ChatRoomClient({
const loadMore = useCallback(async () => {
if (!canView || !hasMore || loadingMore || messages.length === 0) return;
setLoadingMore(true);
- const prevHeight = listRef.current?.scrollHeight ?? 0;
+ const el0 = listRef.current;
+ const prevHeight = el0?.scrollHeight ?? 0;
+ const prevTop = el0?.scrollTop ?? 0;
try {
const before = messages[0].id;
const res = await apiListChatMessages(roomId, before, 30);
@@ -509,7 +511,8 @@ export default function ChatRoomClient({
requestAnimationFrame(() => {
const el = listRef.current;
if (!el) return;
- el.scrollTop = el.scrollHeight - prevHeight;
+ // column-reverse:历史插在上方,用高度差补偿保持视口锚点
+ el.scrollTop = prevTop + (el.scrollHeight - prevHeight);
});
} else {
setHasMore(false);
@@ -524,10 +527,11 @@ export default function ChatRoomClient({
const handleScroll = useCallback(() => {
const el = listRef.current;
if (!el) return;
- if (el.scrollTop < 60) {
+ // column-reverse:滚向上方历史 ≈ 接近 max scrollTop
+ if (el.scrollHeight - el.scrollTop - el.clientHeight < 60) {
loadMore();
}
- if (el.scrollHeight - el.scrollTop - el.clientHeight < 60) {
+ if (el.scrollTop < 60) {
markReadToLatest();
}
}, [loadMore, markReadToLatest]);
@@ -934,7 +938,7 @@ export default function ChatRoomClient({
style={
embedded
? undefined
- : { height: "calc(100vh - 12rem)", minHeight: "420px" }
+ : { height: "100%", minHeight: 0 }
}
>
{!canView ? (
@@ -1062,7 +1066,7 @@ export default function ChatRoomClient({
) : (
- <>
+
{hasMore && (
)}
diff --git a/frontend/app/chat/[id]/page.tsx b/frontend/app/chat/[id]/page.tsx
index 1ee3e6e..d1ebebe 100644
--- a/frontend/app/chat/[id]/page.tsx
+++ b/frontend/app/chat/[id]/page.tsx
@@ -66,29 +66,27 @@ export default async function ChatRoomPage({ params }: PageProps) {
: room.name;
return (
-