UNPKG

@medalsocial/meda

Version:

Shared Meda UI shell and runtime package.

31 lines (30 loc) 3.16 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { Play } from 'lucide-react'; import { formatClock, formatRelativeOffset } from '../lib/format-time.js'; import { LatencyBadge } from './latency-badge.js'; import { ToolCallBlock } from './tool-call-block.js'; const SPEAKER_LABEL = { user: 'You', assistant: 'Assistant', system: 'System', }; export function TurnCard({ turn, startedAtRef, tz, onPlay, className }) { const label = turn.speakerLabel ?? SPEAKER_LABEL[turn.speaker]; const offset = turn.startedAt - startedAtRef; // amber-700 (not -500) for the 11px system speaker label so it clears // WCAG AA (4.5:1) against the card background. let speakerColor; if (turn.speaker === 'assistant') { speakerColor = 'text-primary'; } else if (turn.speaker === 'system') { speakerColor = 'text-amber-700'; } else { speakerColor = 'text-foreground'; } return (_jsxs("div", { className: [ 'grid grid-cols-[64px_1fr] gap-5 border-b border-border py-4 last:border-b-0', className ?? '', ].join(' '), children: [_jsxs("div", { className: "pt-1 text-[11px] tabular-nums text-muted-foreground", children: [_jsx("strong", { className: "mb-0.5 block text-[12px] font-medium text-muted-foreground", children: formatClock(new Date(turn.startedAt), { tz }) }), formatRelativeOffset(offset)] }), _jsxs("div", { children: [_jsxs("div", { className: "mb-1.5 flex items-center gap-2", children: [_jsx("span", { className: ['text-[11px] font-semibold uppercase tracking-wider', speakerColor].join(' '), children: label }), turn.modelLabel && (_jsx("span", { className: "rounded bg-muted px-1.5 py-px text-[10px] font-medium text-muted-foreground", children: turn.modelLabel })), turn.spokenSeconds != null && turn.speaker === 'user' && (_jsxs("span", { className: "rounded bg-muted px-1.5 py-px text-[10px] font-medium text-muted-foreground", children: ["spoken \u00B7 ", turn.spokenSeconds.toFixed(1), "s"] }))] }), _jsxs("p", { className: "text-[15px] leading-relaxed text-foreground", "data-streaming": String(turn.streaming ?? false), children: [turn.text, turn.streaming && (_jsx("span", { "aria-hidden": "true", "data-chromatic": "ignore", className: "ml-0.5 animate-pulse text-primary", children: "\u258D" }))] }), turn.toolCalls?.map((c) => (_jsx(ToolCallBlock, { call: c }, c.id))), (turn.audioUrl || turn.latency) && (_jsxs("div", { className: "mt-2.5 flex flex-wrap items-center gap-4 text-[11px] text-muted-foreground", children: [turn.audioUrl && (_jsx("button", { type: "button", "aria-label": "Play this turn", onClick: () => onPlay?.(turn.id), className: "inline-flex size-6 items-center justify-center rounded-full bg-muted text-muted-foreground hover:text-foreground", children: _jsx(Play, { className: "size-3" }) })), turn.latency?.sttMs != null && _jsx(LatencyBadge, { kind: "stt", ms: turn.latency.sttMs }), turn.latency?.claudeMs != null && (_jsx(LatencyBadge, { kind: "claude", ms: turn.latency.claudeMs })), turn.latency?.ttsMs != null && _jsx(LatencyBadge, { kind: "tts", ms: turn.latency.ttsMs })] }))] })] })); }