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]}
)}
>
);
}