'use client' import { useState } from 'react' type MessageContentPart = | { type: 'text'; text: string } | { type: 'thinking'; thinking: string } | { type: 'tool_use'; id: string; name: string; input: string } | { type: 'tool_result'; toolUseId: string; content: string; isError?: boolean } export type SessionTranscriptMessage = { role: 'user' | 'assistant' | 'system' parts: MessageContentPart[] timestamp?: string } interface SessionMessageProps { message: SessionTranscriptMessage showTimestamp: boolean } const ROLE_CONFIG = { user: { indicator: '$', indicatorClass: 'text-green-400', borderClass: 'border-l-green-500/40' }, assistant: { indicator: '\u25C6', indicatorClass: 'text-primary', borderClass: 'border-l-primary/40' }, system: { indicator: '', indicatorClass: '', borderClass: 'border-l-amber-500/20' }, } as const export function SessionMessage({ message, showTimestamp }: SessionMessageProps) { const config = ROLE_CONFIG[message.role] const timeStr = message.timestamp ? formatTime(message.timestamp) : '' return (
{content}
{code}
{part.slice(1, -1)}
)
}
// Regular text with formatting
return {renderInlineFormatting(part)}
})
}
function renderInlineFormatting(text: string): React.ReactNode[] {
// Process line by line to handle headers, lists, and inline formatting
const lines = text.split('\n')
const result: React.ReactNode[] = []
for (let i = 0; i < lines.length; i++) {
if (i > 0) result.push('\n')
const line = lines[i]
// Headers
const headerMatch = line.match(/^(#{1,3})\s+(.+)/)
if (headerMatch) {
const level = headerMatch[1].length
const headerClass = level === 1 ? 'text-sm font-bold' : level === 2 ? 'text-xs font-semibold' : 'text-xs font-medium'
result.push({renderInlineText(headerMatch[2])})
continue
}
// List items
const listMatch = line.match(/^(\s*)([-*]|\d+\.)\s+(.+)/)
if (listMatch) {
const indent = listMatch[1].length
const bullet = listMatch[2].match(/\d/) ? listMatch[2] : '\u2022'
result.push(
{bullet} {renderInlineText(listMatch[3])}
)
continue
}
result.push({renderInlineText(line)})
}
return result
}
function renderInlineText(text: string): React.ReactNode[] {
// Bold, italic, links
const parts = text.split(/(\*\*[^*]+\*\*|\*[^*]+\*|\[[^\]]+\]\([^)]+\))/g)
return parts.map((segment, j) => {
if (segment.startsWith('**') && segment.endsWith('**')) {
return {segment.slice(2, -2)}
}
if (segment.startsWith('*') && segment.endsWith('*') && !segment.startsWith('**')) {
return {segment.slice(1, -1)}
}
const linkMatch = segment.match(/^\[([^\]]+)\]\(([^)]+)\)$/)
if (linkMatch) {
return (
{linkMatch[1]}
)
}
return segment
})
}