88 lines
3.6 KiB
TypeScript
88 lines
3.6 KiB
TypeScript
import { useState } from 'react'
|
|
import type { SessionEvent } from '../../../api/types/session'
|
|
|
|
function truncate(s: string, n: number): string {
|
|
return s.length > n ? s.slice(0, n) + '…' : s
|
|
}
|
|
|
|
interface Props {
|
|
event: SessionEvent
|
|
defaultCollapsed: boolean
|
|
}
|
|
|
|
export function EventBlock({ event, defaultCollapsed }: Props) {
|
|
const [collapsed, setCollapsed] = useState(defaultCollapsed)
|
|
|
|
const base = 'rounded border px-3 py-2 my-1 text-sm'
|
|
switch (event.type) {
|
|
case 'user':
|
|
if (event.tool_result !== null && event.tool_result !== undefined) {
|
|
return (
|
|
<div id={`evt-${event.index}`} className={`${base} bg-slate-900/40 border-slate-700`}>
|
|
<button onClick={() => setCollapsed(!collapsed)} className="text-xs text-slate-500 uppercase">
|
|
tool_result {collapsed ? '▸' : '▾'}
|
|
</button>
|
|
{!collapsed && (
|
|
<pre className="mt-2 text-xs overflow-x-auto whitespace-pre-wrap text-slate-300">
|
|
{typeof event.tool_result === 'string' ? event.tool_result : JSON.stringify(event.tool_result, null, 2)}
|
|
</pre>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
return (
|
|
<div id={`evt-${event.index}`} className={`${base} bg-blue-500/10 border-blue-500/30`}>
|
|
<div className="text-xs text-blue-300 uppercase mb-1">user</div>
|
|
<div className="whitespace-pre-wrap text-slate-100">{event.text ?? '—'}</div>
|
|
</div>
|
|
)
|
|
case 'assistant':
|
|
if (event.tool_name) {
|
|
return (
|
|
<div id={`evt-${event.index}`} className={`${base} bg-amber-500/10 border-amber-500/30`}>
|
|
<button onClick={() => setCollapsed(!collapsed)} className="text-xs text-amber-300 uppercase">
|
|
tool_use: {event.tool_name} {collapsed ? '▸' : '▾'}
|
|
</button>
|
|
{!collapsed && event.tool_input && (
|
|
<pre className="mt-2 text-xs overflow-x-auto whitespace-pre-wrap text-slate-300">
|
|
{JSON.stringify(event.tool_input, null, 2)}
|
|
</pre>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
return (
|
|
<div id={`evt-${event.index}`} className={`${base} bg-slate-800/40 border-slate-700`}>
|
|
<div className="text-xs text-slate-500 uppercase mb-1">assistant</div>
|
|
<div className="whitespace-pre-wrap text-slate-200">{truncate(event.text ?? '—', collapsed ? 300 : Number.MAX_SAFE_INTEGER)}</div>
|
|
{(event.text?.length ?? 0) > 300 && (
|
|
<button onClick={() => setCollapsed(!collapsed)} className="text-xs text-slate-500 mt-1">
|
|
{collapsed ? 'Expandir' : 'Colapsar'}
|
|
</button>
|
|
)}
|
|
</div>
|
|
)
|
|
case 'system':
|
|
return (
|
|
<div id={`evt-${event.index}`} className={`${base} bg-slate-900/30 border-slate-800 text-xs text-slate-500`}>
|
|
<button onClick={() => setCollapsed(!collapsed)} className="uppercase">
|
|
system {event.skill_invoked ? `· skill: ${event.skill_invoked}` : ''} {event.hook_name ? `· hook: ${event.hook_name}` : ''} {collapsed ? '▸' : '▾'}
|
|
</button>
|
|
{!collapsed && <div className="mt-2 whitespace-pre-wrap">{event.text ?? JSON.stringify(event.raw)}</div>}
|
|
</div>
|
|
)
|
|
case 'attachment':
|
|
return (
|
|
<div id={`evt-${event.index}`} className={`${base} bg-purple-500/10 border-purple-500/30 text-xs text-purple-300`}>
|
|
📎 attachment
|
|
</div>
|
|
)
|
|
default:
|
|
return (
|
|
<div id={`evt-${event.index}`} className={`${base} bg-slate-900/20 border-slate-800 text-xs text-slate-500`}>
|
|
{event.type}
|
|
</div>
|
|
)
|
|
}
|
|
}
|