(msg: Message, options: ExportOptions)
| 11 | } |
| 12 | |
| 13 | function renderMessageHtml(msg: Message, options: ExportOptions): string { |
| 14 | const isUser = msg.role === "user"; |
| 15 | const isError = msg.status === "error"; |
| 16 | |
| 17 | const roleClass = isUser ? "user" : isError ? "error" : "assistant"; |
| 18 | const roleLabel = |
| 19 | msg.role.charAt(0).toUpperCase() + msg.role.slice(1); |
| 20 | |
| 21 | let contentHtml = ""; |
| 22 | |
| 23 | if (typeof msg.content === "string") { |
| 24 | contentHtml = `<p class="message-text">${escapeHtml(msg.content)}</p>`; |
| 25 | } else { |
| 26 | const parts: string[] = []; |
| 27 | for (const block of msg.content) { |
| 28 | if (block.type === "text") { |
| 29 | parts.push(`<p class="message-text">${escapeHtml(block.text)}</p>`); |
| 30 | } else if (block.type === "tool_use" && options.includeToolUse) { |
| 31 | parts.push(` |
| 32 | <details class="tool-block"> |
| 33 | <summary class="tool-summary">Tool: <code>${escapeHtml(block.name)}</code></summary> |
| 34 | <pre class="tool-code">${escapeHtml(JSON.stringify(block.input, null, 2))}</pre> |
| 35 | </details>`); |
| 36 | } else if (block.type === "tool_result" && options.includeToolUse) { |
| 37 | const raw = |
| 38 | typeof block.content === "string" |
| 39 | ? block.content |
| 40 | : extractTextContent(block.content); |
| 41 | const text = |
| 42 | !options.includeFileContents && raw.length > 500 |
| 43 | ? raw.slice(0, 500) + "\n…[truncated]" |
| 44 | : raw; |
| 45 | parts.push(` |
| 46 | <details class="tool-block${block.is_error ? " tool-error" : ""}"> |
| 47 | <summary class="tool-summary">Tool Result${block.is_error ? " (error)" : ""}</summary> |
| 48 | <pre class="tool-code">${escapeHtml(text)}</pre> |
| 49 | </details>`); |
| 50 | } |
| 51 | } |
| 52 | contentHtml = parts.join("\n"); |
| 53 | } |
| 54 | |
| 55 | const timestampHtml = options.includeTimestamps |
| 56 | ? `<span class="message-time">${new Date(msg.createdAt).toLocaleString()}</span>` |
| 57 | : ""; |
| 58 | |
| 59 | return ` |
| 60 | <div class="message message--${roleClass}"> |
| 61 | <div class="message-header"> |
| 62 | <span class="message-role">${escapeHtml(roleLabel)}</span> |
| 63 | ${timestampHtml} |
| 64 | </div> |
| 65 | <div class="message-content"> |
| 66 | ${contentHtml} |
| 67 | </div> |
| 68 | </div>`; |
| 69 | } |
| 70 |
no test coverage detected