File size: 2,227 Bytes
b2c86fd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | /**
* 渲染单条消息
* - user 消息:简单文本
* - assistant 消息:渲染 steps 流(thinking / code / tool_use / result / error)
*/
export default function MessageItem({ message }) {
if (message.role === "user") {
return (
<div className="message user-message">
<div className="message-content">{message.content}</div>
</div>
);
}
// assistant
return (
<div className="message assistant-message">
{message.steps.map((step, i) => (
<StepBlock key={i} step={step} />
))}
{!message.done && message.steps.length === 0 && (
<span className="placeholder">Agent is thinking…</span>
)}
</div>
);
}
function StepBlock({ step }) {
switch (step.type) {
case "thinking":
return (
<div className="step thinking-step">
<div className="result-label">💭 Reasoning</div>
<pre>{step.content}</pre>
</div>
);
case "code":
return (
<div className="step code-step">
<div className="code-header">
<span className="code-lang">{step.lang}</span>
<button onClick={() => navigator.clipboard.writeText(step.content)}>
Copy
</button>
</div>
<pre className="code-block">{step.content}</pre>
</div>
);
case "tool_use":
return (
<div className="step tool-step">
🔧 <strong>{step.tool}</strong>
<span className="tool-detail">{step.content?.slice(0, 80)}…</span>
</div>
);
case "observation":
return (
<details className="step observation-step">
<summary>📊 Observation</summary>
<pre>{step.content}</pre>
</details>
);
case "result":
return (
<div className="step result-step">
<div className="result-label">✅ Result</div>
<div className="result-content">{step.content}</div>
</div>
);
case "error":
return (
<details className="step error-step" open>
<summary>❌ Error</summary>
<pre>{step.content}</pre>
</details>
);
default:
return null;
}
}
|