import React, { useState } from "react"; import { ArrowClockwise, ArrowSquareOut, Check, ChatCircle, CopySimple, Trash, Sparkle, User, NotePencil, } from "@phosphor-icons/react"; import { IconButton, Empty, Spinner } from "./ui.jsx"; import { Markdown } from "./Markdown.jsx"; import { messageSources } from "./messageSources.js"; export function AssistantTranscript({ loading, messages, material, busy, selectedChat, errors, transcriptRef, nearBottom, below, setBelow, onRemoveMessage, onRetry, onPage, notify, onSaveNote, onComparisonPage, }) { const [copiedMessageId, setCopiedMessageId] = useState(null); const retryableQuestion = [...messages] .reverse() .find((message) => message.role === "user"); const copyMessage = async (message) => { try { await navigator.clipboard.writeText(message.content); setCopiedMessageId(message.id); window.setTimeout( () => setCopiedMessageId((current) => current === message.id ? null : current, ), 1500, ); } catch { notify("Clipboard access was denied.", "error"); } }; return ( <>
{ const element = transcriptRef.current; nearBottom.current = element.scrollHeight - element.scrollTop - element.clientHeight < 100; if (nearBottom.current) setBelow(false); }} aria-label="Conversation messages" aria-live="polite" aria-relevant="additions" > {loading ? ( ) : !messages.length ? ( Select a passage or ask a question about this{" "} {material?.kind === "book" ? "book" : "article"}. ) : ( messages.map((message, index) => (
{message.role === "user" ? ( ) : ( )} {message.role === "user" ? "You" : message.model || "Assistant"}
onRemoveMessage(message)} > {message.role === "user" && message.context?.selection && (
Selected passage {message.context.page ? " · page " + message.context.page : ""}
{message.context.selection}
)} {message.content ? ( onComparisonPage( message.citations.find( (c) => c.comparison && c.page === page, )?.materialId, page, ) } /> ) : message.status === "streaming" ? ( Thinking… ) : ( No answer was saved. )} {message.role === "assistant" && (
{message.content && message.status === "complete" && ( )} {message.status !== "complete" && message.status !== "streaming" && ( {message.status} )} {message.content && ( )} {!busy[selectedChat] && index === messages.length - 1 && retryableQuestion && ( )}
)} {message.role === "assistant" && message.citations?.length > 0 && (
Context used {message.context?.estimatedTokens ? " · ~" + message.context.estimatedTokens.toLocaleString() + " tokens" : ""}
{message.citations.map((citation, citationIndex) => citation.page ? ( ) : ( {citation.label} ), )}
)}
)) )}
{below && ( )} {errors[selectedChat] && (
{errors[selectedChat]}
)} ); }