({ message }: { message: SessionMessage })
| 157 | } |
| 158 | |
| 159 | function MessageBubble({ message }: { message: SessionMessage }) { |
| 160 | const isUser = message.role === 'user'; |
| 161 | |
| 162 | return ( |
| 163 | <div className={cn( |
| 164 | 'flex gap-3', |
| 165 | isUser && 'flex-row-reverse' |
| 166 | )}> |
| 167 | <div className={cn( |
| 168 | 'w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0', |
| 169 | isUser ? 'bg-[var(--color-primary)]/20' : 'bg-[var(--color-accent)]/20' |
| 170 | )}> |
| 171 | {isUser ? ( |
| 172 | <User className="w-4 h-4 text-[var(--color-primary)]" /> |
| 173 | ) : ( |
| 174 | <Bot className="w-4 h-4 text-[var(--color-accent)]" /> |
| 175 | )} |
| 176 | </div> |
| 177 | <div className={cn( |
| 178 | 'flex-1 max-w-[80%]', |
| 179 | isUser && 'text-right' |
| 180 | )}> |
| 181 | <div className={cn( |
| 182 | 'inline-block p-3 rounded-lg text-sm', |
| 183 | isUser |
| 184 | ? 'bg-[var(--color-primary)] text-white rounded-tr-none' |
| 185 | : 'bg-[var(--color-surface-elevated)] text-[var(--color-text-primary)] rounded-tl-none' |
| 186 | )}> |
| 187 | <p className="whitespace-pre-wrap break-words"> |
| 188 | {message.content || '(空消息)'} |
| 189 | </p> |
| 190 | </div> |
| 191 | <p className={cn( |
| 192 | 'text-xs text-[var(--color-text-muted)] mt-1', |
| 193 | isUser && 'text-right' |
| 194 | )}> |
| 195 | {formatRelativeTime(message.timestamp)} |
| 196 | </p> |
| 197 | </div> |
| 198 | </div> |
| 199 | ); |
| 200 | } |
nothing calls this directly
no test coverage detected