- applications/iknowyou: new Next.js chat application with persona-aware conversations, briefing API, cohort logic, vLLM streaming, and sidebar navigation - tidal M8: add replication control plane (control.rs), tenant migration state machine (migration.rs), tenant/upgrade coordinators, cluster/fault test harnesses - tidal M8 tests: expand m8p2/m8p3/m8p4 test suites; add m8p5_multitenancy and m8_uat - tidal db: split replication_ops out of db/mod.rs (was 647 lines, now 574) - .claude: add kai-park, kaya-osei, mira-vasquez agents; add aeries-design-architect, aeries-fullstack-engineer, aeries-product-visionary skills - docs: update ROADMAP.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, useCallback } from "react";
|
|
import { useChatStore } from "@/lib/store";
|
|
import { Message } from "./message";
|
|
|
|
export function MessageList() {
|
|
const messages = useChatStore((s) => s.messages);
|
|
const isStreaming = useChatStore((s) => s.isStreaming);
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const isNearBottomRef = useRef(true);
|
|
|
|
const scrollToBottom = useCallback(() => {
|
|
if (!containerRef.current || !isNearBottomRef.current) return;
|
|
containerRef.current.scrollTop = containerRef.current.scrollHeight;
|
|
}, []);
|
|
|
|
const handleScroll = useCallback(() => {
|
|
if (!containerRef.current) return;
|
|
const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
|
|
isNearBottomRef.current = scrollHeight - scrollTop - clientHeight < 100;
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
scrollToBottom();
|
|
}, [messages, scrollToBottom]);
|
|
|
|
if (messages.length === 0) {
|
|
return (
|
|
<div className="flex-1 flex items-center justify-center">
|
|
<div className="text-center">
|
|
<p className="text-text-muted text-lg">aeries</p>
|
|
<p className="text-text-faint text-sm mt-2">say something</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div
|
|
ref={containerRef}
|
|
onScroll={handleScroll}
|
|
className="flex-1 overflow-y-auto px-4 py-6 md:px-8"
|
|
>
|
|
<div className="max-w-2xl mx-auto space-y-0">
|
|
{messages.map((msg, i) => {
|
|
const next = messages[i + 1];
|
|
const isLastInGroup = !next || next.role !== msg.role;
|
|
const isCurrentlyStreaming =
|
|
isStreaming &&
|
|
msg.role === "assistant" &&
|
|
i === messages.length - 1;
|
|
|
|
return (
|
|
<Message
|
|
key={msg.id}
|
|
message={msg}
|
|
isStreaming={isCurrentlyStreaming}
|
|
isLastInGroup={isLastInGroup}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|