web(coder UI): ChatInput migration + Thinking render + DiffPanel route fix
Bundles in-progress working-tree UI work not authored this session (CoderPane ChatInput migration, AgentComposerBar/CoderMessageList/tab-bar/sidebar/pane refinements, provider icons) with this session's changes to the same files: MessageBubble renders a collapsible 'Thinking' block from reasoning_text/reasoning_parts (surfacing ACP agent_thought_chunk + native reasoning), and the DiffPanel approve/reject calls are repointed to the real /api/coder/pending/:id/apply and /reject routes (the old /sessions/:id/pending/:id/approve|reject paths did not exist). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, type ReactNode } from 'react';
|
||||
import { MarkdownRenderer } from '@/components/MarkdownRenderer';
|
||||
import { MessageBubble, type MessageActions } from '@/components/MessageBubble';
|
||||
import { ToolCallGroup } from '@/components/ToolCallGroup';
|
||||
import { ToolCallLine, type ToolRun } from '@/components/ToolCallLine';
|
||||
import { AskUserInputCard } from '@/components/AskUserInputCard';
|
||||
import { wireToolCallToRun, type CoderToolCallWire } from '@/lib/coder-tools';
|
||||
import type { Message } from '@/api/types';
|
||||
|
||||
export interface CoderMessageWire {
|
||||
id: string;
|
||||
@@ -141,54 +142,16 @@ function groupToolRuns(items: RenderItem[]): RenderItem[] {
|
||||
return out;
|
||||
}
|
||||
|
||||
function CoderTextBubble({ message }: { message: CoderMessageWire }) {
|
||||
const isUser = message.role === 'user';
|
||||
const isStreaming = message.status === 'streaming';
|
||||
const hasText = message.content.trim().length > 0;
|
||||
const hasReasoning = (message.reasoning_text?.trim().length ?? 0) > 0;
|
||||
|
||||
if (isUser) {
|
||||
return (
|
||||
<div className="flex flex-col items-end gap-1">
|
||||
<div className="max-w-[80%] rounded-lg bg-primary text-primary-foreground px-3 py-2 text-sm whitespace-pre-wrap break-words min-w-0">
|
||||
{message.content}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
{hasReasoning && (
|
||||
<details className="rounded border border-border/40 bg-muted/20 px-2 py-1">
|
||||
<summary className="cursor-pointer text-xs text-muted-foreground select-none">Reasoning</summary>
|
||||
<pre className="mt-1 max-h-48 overflow-y-auto whitespace-pre-wrap text-[11px] text-muted-foreground font-mono">
|
||||
{message.reasoning_text}
|
||||
</pre>
|
||||
</details>
|
||||
)}
|
||||
{(hasText || (isStreaming && !hasReasoning)) && (
|
||||
<div className="max-w-[90%] text-sm leading-relaxed space-y-2 break-words min-w-0">
|
||||
{hasText ? <MarkdownRenderer content={message.content} /> : null}
|
||||
{isStreaming && (
|
||||
<span className="inline-block w-1.5 h-3.5 align-baseline bg-muted-foreground/60 animate-pulse" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{message.status === 'failed' && (
|
||||
<div className="text-xs text-destructive">message failed</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
messages: CoderTimelineWire[];
|
||||
chatId?: string;
|
||||
footer?: ReactNode;
|
||||
actions?: MessageActions;
|
||||
}
|
||||
|
||||
export function CoderMessageList({ messages, chatId, footer }: Props) {
|
||||
const CODER_HIDDEN_ACTIONS: ('fork' | 'delete' | 'openInPane')[] = ['fork', 'openInPane'];
|
||||
|
||||
export function CoderMessageList({ messages, chatId, footer, actions }: Props) {
|
||||
const endRef = useRef<HTMLDivElement>(null);
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
const isNearBottomRef = useRef(true);
|
||||
@@ -220,7 +183,14 @@ export function CoderMessageList({ messages, chatId, footer }: Props) {
|
||||
<div className="max-w-[1000px] mx-auto w-full px-6 py-4 space-y-4">
|
||||
{renderItems.map((item) => {
|
||||
if (item.kind === 'message') {
|
||||
return <CoderTextBubble key={item.message.id} message={item.message} />;
|
||||
return (
|
||||
<MessageBubble
|
||||
key={item.message.id}
|
||||
message={item.message as unknown as Message}
|
||||
actions={actions}
|
||||
hideActions={CODER_HIDDEN_ACTIONS}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (item.kind === 'tool_run') {
|
||||
if (item.run.call.name === 'ask_user_input' && chatId) {
|
||||
|
||||
Reference in New Issue
Block a user