diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 1380a8b9d4b3175b46a9e6b9f5f44cef0a7118e8..446831c8a9c609cd68e6c78e1ea8b47bb48f9116 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -891,8 +891,9 @@ export default function ChatView({ threadId }: ChatViewProps) { latestTurnSettled && activeProposedPlan !== null; const activePendingApproval = pendingApprovals[0] ?? null; + const isComposerApprovalState = activePendingApproval !== null; const hasComposerHeader = - activePendingApproval !== null || + isComposerApprovalState || pendingUserInputs.length > 0 || (showPlanFollowUpPrompt && activeProposedPlan !== null); useEffect(() => { @@ -3381,8 +3382,6 @@ export default function ChatView({ threadId }: ChatViewProps) { ) : pendingUserInputs.length > 0 ? ( @@ -3411,7 +3410,7 @@ export default function ChatView({ threadId }: ChatViewProps) { hasComposerHeader ? "pt-2.5 sm:pt-3" : "pt-3.5 sm:pt-4", )} > - {composerMenuOpen && ( + {composerMenuOpen && !isComposerApprovalState && (
)} - {pendingUserInputs.length === 0 && composerImages.length > 0 && ( + {!isComposerApprovalState && pendingUserInputs.length === 0 && composerImages.length > 0 && (
{composerImages.map((image) => (
{/* Bottom toolbar */} -
-
- {/* Provider/model picker */} - + - - {selectedProvider === "codex" && selectedEffort != null ? ( - <> - - - - ) : null} - - {/* Divider */} - - - {/* Interaction mode toggle */} - - - {/* Divider */} - - - {/* Runtime mode toggle */} -
+ ) : ( +
+
+ {/* Provider/model picker */} + - {/* Right side: send / stop button */} -
- {isPreparingWorktree ? ( - Preparing worktree... - ) : null} - {activePendingProgress ? ( -
- {activePendingProgress.questionIndex > 0 ? ( - - ) : null} - -
- ) : phase === "running" ? ( - - ) : pendingUserInputs.length === 0 ? ( - showPlanFollowUpPrompt ? ( - prompt.trim().length > 0 ? ( + + + {interactionMode === "plan" ? "Plan" : "Chat"} + + + + {/* Divider */} + + + {/* Runtime mode toggle */} + +
+ + {/* Right side: send / stop button */} +
+ {isPreparingWorktree ? ( + Preparing worktree... + ) : null} + {activePendingProgress ? ( +
+ {activePendingProgress.questionIndex > 0 ? ( + + ) : null} - ) : ( -
+
+ ) : phase === "running" ? ( + + ) : pendingUserInputs.length === 0 ? ( + showPlanFollowUpPrompt ? ( + prompt.trim().length > 0 ? ( - - + + + + } + > + + + + - } + onClick={() => void onImplementPlanInNewThread()} + > + Implement in new thread + + + +
+ ) + ) : ( +
+ + + ) : ( + + )} + ) - ) : ( - - ) - ) : null} + ) : null} +
-
+ )}
@@ -4000,18 +4017,11 @@ const ProviderHealthBanner = memo(function ProviderHealthBanner({ interface ComposerPendingApprovalPanelProps { approval: PendingApproval; pendingCount: number; - isResponding: boolean; - onRespondToApproval: ( - requestId: ApprovalRequestId, - decision: ProviderApprovalDecision, - ) => Promise; } const ComposerPendingApprovalPanel = memo(function ComposerPendingApprovalPanel({ approval, pendingCount, - isResponding, - onRespondToApproval, }: ComposerPendingApprovalPanelProps) { const approvalSummary = approval.requestKind === "command" @@ -4029,47 +4039,62 @@ const ComposerPendingApprovalPanel = memo(function ComposerPendingApprovalPanel( 1/{pendingCount} ) : null} -

- {approval.detail} -

-
- - - - -
); }); +interface ComposerPendingApprovalActionsProps { + requestId: ApprovalRequestId; + isResponding: boolean; + onRespondToApproval: ( + requestId: ApprovalRequestId, + decision: ProviderApprovalDecision, + ) => Promise; +} + +const ComposerPendingApprovalActions = memo(function ComposerPendingApprovalActions({ + requestId, + isResponding, + onRespondToApproval, +}: ComposerPendingApprovalActionsProps) { + return ( + <> + + + + + + ); +}); + interface PlanModePanelProps { activePlan: ReturnType; }