@@ -20,7 +20,6 @@ import type {
2020 ModelSelection ,
2121 ProjectId ,
2222 PreviewAnnotationPayload ,
23- ProviderApprovalDecision ,
2423 ThreadContextRecord ,
2524 EnvironmentContextRecord ,
2625 ProviderInteractionMode ,
@@ -187,7 +186,6 @@ import {
187186 ComposerCommandMenu ,
188187 composerSuggestionOptionId ,
189188} from "./ComposerCommandMenu" ;
190- import { ComposerPendingApprovalActions } from "./ComposerPendingApprovalActions" ;
191189import {
192190 ComposerAttachButton ,
193191 ComposerAttachmentFileList ,
@@ -198,7 +196,6 @@ import {
198196} from "./ComposerAttachmentStrip" ;
199197import { ComposerPrimaryActions } from "./ComposerPrimaryActions" ;
200198import { ComposerFooterPrimaryActions } from "./ComposerFooterControls" ;
201- import { ComposerPendingApprovalPanel } from "./ComposerPendingApprovalPanel" ;
202199import { ComposerPendingUserInputPanel } from "./ComposerPendingUserInputPanel" ;
203200import { ComposerPlanFollowUpBanner } from "./ComposerPlanFollowUpBanner" ;
204201import { resolveComposerMenuActiveItemId } from "./composerMenuHighlight" ;
@@ -239,7 +236,7 @@ const isEnvironmentRequestInvalidError = Schema.is(EnvironmentRequestInvalidErro
239236const COMPOSER_SCROLL_COLLAPSE_THRESHOLD_PX = 24 ;
240237const COMPOSER_SCROLL_GESTURE_RESET_MS = 120 ;
241238import { toastManager } from "../ui/toast" ;
242- import { PlayIcon , ShieldIcon } from "lucide-react" ;
239+ import { PlayIcon } from "lucide-react" ;
243240import { proposedPlanTitle } from "@t3tools/shared/proposedPlanText" ;
244241import { hasProviderSetup } from "./ProviderStatusBanner" ;
245242import { type AppModelOption , getAppModelOptionsForInstance } from "../../modelSelection" ;
@@ -402,6 +399,7 @@ export interface ChatComposerProps {
402399
403400 activePendingApproval : PendingApproval | null ;
404401 pendingApprovals : PendingApproval [ ] ;
402+ onHeldRequestIdsChange : ( requestIds : ReadonlySet < string > ) => void ;
405403 pendingUserInputs : PendingUserInput [ ] ;
406404 activePendingProgress : {
407405 questionIndex : number ;
@@ -482,10 +480,6 @@ export interface ChatComposerProps {
482480 onResume : ( ) => void ;
483481 onInterrupt : ( ) => void ;
484482 onImplementPlanInNewThread : ( ) => void ;
485- onRespondToApproval : (
486- requestId : RuntimeRequestId ,
487- decision : ProviderApprovalDecision ,
488- ) => Promise < unknown > ;
489483 onSelectActivePendingUserInputOption : ( questionId : string , optionValue : string ) => void ;
490484 onAdvanceActivePendingUserInput : ( ) => void ;
491485 onDismissActivePendingUserInput : ( requestId : RuntimeRequestId ) => void ;
@@ -550,6 +544,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
550544 environmentUnavailable,
551545 activePendingApproval : incomingPendingApproval ,
552546 pendingApprovals,
547+ onHeldRequestIdsChange,
553548 pendingUserInputs : incomingPendingUserInputs ,
554549 activePendingProgress : incomingPendingProgress ,
555550 activePendingResolvedAnswers,
@@ -601,7 +596,6 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
601596 onResume,
602597 onInterrupt,
603598 onImplementPlanInNewThread,
604- onRespondToApproval,
605599 onSelectActivePendingUserInputOption,
606600 onAdvanceActivePendingUserInput,
607601 onDismissActivePendingUserInput,
@@ -633,6 +627,11 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
633627 ...pendingApprovals . map ( ( request ) => request . requestId ) ,
634628 ...incomingPendingUserInputs . map ( ( request ) => request . requestId ) ,
635629 ] ) ;
630+ // A layout effect so the timeline never paints a held approval as answerable.
631+ // oxlint-disable-next-line t3code/no-unreasoned-state-sync -- publishes held requests to the timeline without lifting per-keystroke typing state
632+ useLayoutEffect ( ( ) => {
633+ onHeldRequestIdsChange ( heldRequestIds ) ;
634+ } , [ heldRequestIds , onHeldRequestIdsChange ] ) ;
636635 const activePendingApproval =
637636 incomingPendingApproval && ! heldRequestIds . has ( incomingPendingApproval . requestId )
638637 ? incomingPendingApproval
@@ -1200,7 +1199,6 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
12001199 const isChoiceOnlyPendingQuestion =
12011200 activePendingProgress ?. activeQuestion ?. allowCustomAnswer === false ;
12021201 const showComposerTopDrawer =
1203- isComposerApprovalState ||
12041202 pendingUserInputs . length > 0 ||
12051203 ( ! isComposerCollapsedMobile && showPlanFollowUpPrompt && activeProposedPlan !== null ) ;
12061204 const showCollapsedMobilePromptRow = isComposerCollapsedMobile ;
@@ -2320,8 +2318,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
23202318 importContextRecords,
23212319 expandMobileComposer : expandMobileComposerStable ,
23222320 } ) ;
2323- const hasBlockingComposerTopDrawer =
2324- activePendingApproval !== null || pendingUserInputs . length > 0 ;
2321+ const hasBlockingComposerTopDrawer = pendingUserInputs . length > 0 ;
23252322 const hasImageAttachmentAttention = standaloneComposerImages . some ( ( image ) => {
23262323 const upload = uploadsByImageId [ image . id ] ;
23272324 const failedInCurrentEnvironment =
@@ -2580,7 +2577,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
25802577 getModelDisabledReason = { getModelDisabledReason }
25812578 />
25822579 ) ;
2583- // An open approval or question owns the space above the composer. Only a machine problem,
2580+ // An open question owns the space above the composer. Only a machine problem,
25842581 // which is why an answer could not go through, stays beside it.
25852582 const shelfItems = hasBlockingComposerTopDrawer
25862583 ? props . bannerItems . filter (
@@ -3444,39 +3441,8 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
34443441 < ComposerBannerStack items = { shelfItems } />
34453442 { showComposerTopDrawer ? (
34463443 < ComposerBanner . Attachment >
3447- < ComposerBanner . Root
3448- data-chat-composer-top-drawer = "true"
3449- variant = { activePendingApproval ? "warning" : "info" }
3450- density = { activePendingApproval ? "spacious" : "default" }
3451- >
3452- { activePendingApproval ? (
3453- < ComposerBanner . Row
3454- layout = "approval"
3455- data-chat-composer-collapsed-controls = "true"
3456- >
3457- < ComposerBanner . Icon >
3458- < ShieldIcon />
3459- </ ComposerBanner . Icon >
3460- < ComposerBanner . Content >
3461- < ComposerPendingApprovalPanel
3462- approval = { activePendingApproval }
3463- pendingCount = { pendingApprovals . length }
3464- />
3465- </ ComposerBanner . Content >
3466- < ComposerBanner . Actions >
3467- < ComposerPendingApprovalActions
3468- requestId = { activePendingApproval . requestId }
3469- disabled = { ! canOperateThread }
3470- isResponding = { respondingRequestIds . includes (
3471- activePendingApproval . requestId ,
3472- ) }
3473- options = { activePendingApproval . options }
3474- canRespond = { activePendingApproval . responseCapability === "live" }
3475- onRespondToApproval = { onRespondToApproval }
3476- />
3477- </ ComposerBanner . Actions >
3478- </ ComposerBanner . Row >
3479- ) : pendingUserInputs . length > 0 ? (
3444+ < ComposerBanner . Root data-chat-composer-top-drawer = "true" variant = "info" >
3445+ { pendingUserInputs . length > 0 ? (
34803446 < ComposerPendingUserInputPanel
34813447 pendingUserInputs = { pendingUserInputs }
34823448 cwd = { gitCwd ?? undefined }
0 commit comments