// ChatPane — mirrors ui/src/components/chat/* with user/ask/result/progress bubbles
function Bubble({ role, children, footer }) {
const isUser = role === 'user';
const style = {
maxWidth: '85%',
padding: '10px 14px',
borderRadius: 16,
fontSize: 13,
lineHeight: 1.55,
whiteSpace: 'pre-wrap',
wordBreak: 'break-word',
};
if (isUser) {
Object.assign(style, { background: '#f1f5f9', color: '#0f172a', borderBottomRightRadius: 4, alignSelf: 'flex-end' });
} else if (role === 'ask') {
Object.assign(style, { background: '#fef9c3', color: '#854d0e', border: '1px solid #fde68a', borderBottomLeftRadius: 4 });
} else if (role === 'result') {
Object.assign(style, { background: '#ecfdf5', color: '#065f46', border: '1px solid #a7f3d0', borderBottomLeftRadius: 4 });
} else {
Object.assign(style, { background: '#fff', color: '#0f172a', border: '1px solid #e2e8f0', borderBottomLeftRadius: 4 });
}
return (
{children}
{footer &&
{footer}
}
);
}
function ProgressBubble({ text }) {
return (
{text}
);
}
function ChatHeader({ task, onOpenDetail, detailOpen }) {
return (
TASK #{task.id}
{task.title}
);
}
function Composer({ onSend, disabled }) {
const [text, setText] = React.useState('');
const [sending, setSending] = React.useState(false);
const [error, setError] = React.useState(null);
const send = async () => {
if (!text.trim() || disabled || sending) return;
setError(null); setSending(true);
try {
await Promise.resolve(onSend(text.trim()));
setText('');
} catch (e) {
setError(e?.message || '送信に失敗しました');
} finally {
setSending(false);
}
};
return (
{error && (
⚠ {error}
)}
エージェントは常に /brainstorm → /plan → /implement のパイプラインで動作します。
);
}
function ChatPane({ task, messages, onSend, onOpenDetail, detailOpen, loading, onOpenCreate }) {
const scrollRef = React.useRef(null);
React.useEffect(() => {
if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
}, [messages.length, task && task.id]);
if (!task) {
return (
}
title="タスクを選択してください"
hint="左のリストから会話を開くか、新しい依頼を作成できます。"
action={onOpenCreate && (
)}
/>
);
}
return (
{loading && (
<>
>
)}
{!loading && messages.length === 0 && (
)}
{!loading && messages.map((m, i) => (
m.role === 'progress'
?
:
{m.content}
))}
);
}
window.ChatPane = ChatPane;