import { createElement, createFragment } from "../utils/dom"; import { AgentWidgetMessage, AgentWidgetConfig } from "../types"; import { MessageTransform, MessageActionCallbacks } from "./message-bubble"; import { createStandardBubble } from "./message-bubble"; import { createReasoningBubble } from "./reasoning-bubble"; import { createToolBubble } from "./tool-bubble"; import { ensureAskUserQuestionSheet, isAskUserQuestionMessage, removeAskUserQuestionSheet, } from "./ask-user-question-bubble"; import { isSuggestRepliesMessage } from "../suggest-replies-tool"; export const renderMessages = ( container: HTMLElement, messages: AgentWidgetMessage[], transform: MessageTransform, showReasoning: boolean, showToolCalls: boolean, config?: AgentWidgetConfig, actionCallbacks?: MessageActionCallbacks, composerOverlay?: HTMLElement | null ) => { container.innerHTML = ""; const fragment = createFragment(); // Track which ask_user_question tool-call ids are currently in the message // list, so we can prune stale sheets from the overlay afterward. const liveAskToolIds = new Set(); messages.forEach((message) => { let bubble: HTMLElement; if (message.variant === "reasoning" && message.reasoning) { if (!showReasoning) return; bubble = createReasoningBubble(message, config); } else if (isAskUserQuestionMessage(message)) { // No transcript bubble: the overlay sheet is the only question UI. if (config?.features?.askUserQuestion?.enabled === false) return; if (!message.agentMetadata?.askUserQuestionAnswered) { if (message.toolCall?.id) liveAskToolIds.add(message.toolCall.id); ensureAskUserQuestionSheet(message, config, composerOverlay ?? null); } return; } else if ( isSuggestRepliesMessage(message) && config?.features?.suggestReplies?.enabled !== false ) { // No transcript bubble: the chips above the composer are the only UI. // When the feature is disabled the message falls through to the generic // tool bubble below (and is never auto-resumed), making the parked // execution visible instead of silently swallowed. return; } else if (message.variant === "tool" && message.toolCall) { if (!showToolCalls) return; bubble = createToolBubble(message, config); } else { bubble = createStandardBubble( message, transform, config?.layout?.messages, config?.messageActions, actionCallbacks ); } const wrapper = createElement("div", "persona-flex"); if (message.role === "user") { wrapper.classList.add("persona-justify-end"); } wrapper.appendChild(bubble); fragment.appendChild(wrapper); }); container.appendChild(fragment); container.scrollTop = container.scrollHeight; // Clean up any orphaned ask_user_question sheets whose source message is no // longer in the list (e.g. after clearChat or a message splice). if (composerOverlay) { const sheets = composerOverlay.querySelectorAll( '[data-persona-ask-sheet-for]' ); sheets.forEach((sheet) => { const id = sheet.getAttribute('data-persona-ask-sheet-for'); if (id && !liveAskToolIds.has(id)) { removeAskUserQuestionSheet(composerOverlay, id); } }); } };