"use client"; import { Button } from "@kandev/ui/button"; import { GridSpinner } from "@/components/grid-spinner"; import type { Message, TaskSessionState, TaskState } from "@/lib/types/http"; import type { RenderItem } from "@/hooks/use-processed-messages"; import { MessageRenderer } from "@/components/task/chat/message-renderer"; import { TurnGroupMessage } from "@/components/task/chat/messages/turn-group-message"; import { PrepareProgress } from "@/components/session/prepare-progress"; export type MessageListProps = { items: RenderItem[]; messages: Message[]; /** Action messages rendered after the env prep error status in the footer. */ footerActionMessages?: Message[]; permissionsByToolCallId: Map; childrenByParentToolCallId: Map; taskId?: string; sessionId: string | null; messagesLoading: boolean; isWorking: boolean; sessionState?: TaskSessionState; taskState?: TaskState; worktreePath?: string; onOpenFile?: (path: string) => void; }; export function getItemKey(item: RenderItem): string { if (item.type === "turn_group" || item.type === "prepare_progress") return item.id; return item.message.id; } export function getSessionRunningState(sessionState: string | null | undefined) { return sessionState === "CREATED" || sessionState === "STARTING" || sessionState === "RUNNING"; } export function getLastTurnGroupId(items: RenderItem[]) { for (let i = items.length - 1; i >= 0; i--) { const item = items[i]; if (item.type === "turn_group") return item.id; } return null; } export function MessageListStatus({ isLoadingMore, hasMore, showLoadingState, messagesLoading, isInitialLoading, messagesCount, onLoadMore, }: { isLoadingMore: boolean; hasMore: boolean; showLoadingState: boolean; messagesLoading: boolean; isInitialLoading: boolean; messagesCount: number; /** * Explicitly load the previous page of older messages. Rendered as a button so * older history is always reachable even when the scroll-up IntersectionObserver * fails to re-arm (e.g. pinned at the very top with the sentinel always in view). */ onLoadMore?: () => void; }) { return ( <> {isLoadingMore && hasMore && (
Loading older messages...
)} {hasMore && !isLoadingMore && onLoadMore && (
)} {showLoadingState && (
Loading messages...
)} {!messagesLoading && !isInitialLoading && messagesCount === 0 && (
No messages yet. Start the conversation!
)} ); } export function MessageItem({ item, sessionId, permissionsByToolCallId, childrenByParentToolCallId, taskId, worktreePath, onOpenFile, isLastGroup, isTurnActive, messages, sessionState, taskState, onScrollToMessage, }: { item: RenderItem; sessionId: string | null; permissionsByToolCallId: Map; childrenByParentToolCallId: Map; taskId?: string; worktreePath?: string; onOpenFile?: (path: string) => void; isLastGroup: boolean; isTurnActive: boolean; messages: Message[]; sessionState?: TaskSessionState; taskState?: TaskState; onScrollToMessage: (id: string) => void; }) { if (item.type === "prepare_progress") { return ; } if (item.type === "turn_group") { return ( ); } return ( ); }