diff --git a/src/renderer/src/components/agent-sign-in-strip/cloud-chat-running-send-block.test.tsx b/src/renderer/src/components/agent-sign-in-strip/cloud-chat-running-send-block.test.tsx index afd4e3a4b14..9e17ad749a8 100644 --- a/src/renderer/src/components/agent-sign-in-strip/cloud-chat-running-send-block.test.tsx +++ b/src/renderer/src/components/agent-sign-in-strip/cloud-chat-running-send-block.test.tsx @@ -88,7 +88,7 @@ const props = { tabId: TAB_ID } as const -function composer(sendBlockedReason: string | null, draft: string) { +function composer(sendBlockedReason: string | null, draft: string, heldHint?: string) { return nativeChatComposerSendState({ agent: 'claude', draft, @@ -99,7 +99,8 @@ function composer(sendBlockedReason: string | null, draft: string) { structuredTransport: { sendOut: false, optionSnapshot: [], - ...(sendBlockedReason !== null ? { sendBlockedReason } : {}) + ...(sendBlockedReason !== null ? { sendBlockedReason } : {}), + ...(heldHint ? { heldHint } : {}) }, editSession: undefined, isWorking: false, @@ -167,10 +168,12 @@ describe('a running idle chat whose strip is expired holds Send with its hint (E expect(slot.heldHint).toBe(HINT) expect(slot.sendBlockedReason).toBe(HINT) for (const draft of ['', 'continue please']) { - const state = composer(slot.sendBlockedReason, draft) + const state = composer(slot.sendBlockedReason, draft, slot.heldHint) expect(state.sendButtonDisabled).toBe(true) expect(state.sendHeld).toBe(true) expect(state.sendBlockedReason).toBe(HINT) + // The composer field shows it as the empty field's placeholder. + expect(state.heldHint).toBe(HINT) } }) } diff --git a/src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx b/src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx index 5fce3d53fb8..c992399a495 100644 --- a/src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx +++ b/src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx @@ -217,7 +217,8 @@ export function NativeChatStructuredSession(props: Omit | undefined editSession: { canSave: boolean } | undefined @@ -90,6 +97,7 @@ export function nativeChatComposerSendState( imageBlock.reason ?? (hostHold && !runsWhileHostHolds ? transport?.sendBlockedReason : null) ?? null, + ...(transport?.heldHint ? { heldHint: transport.heldHint } : {}), sendHeld, hasDraft, sendButtonDisabled, diff --git a/src/renderer/src/components/native-chat/composer/submit/native-chat-structured-composer-transport.ts b/src/renderer/src/components/native-chat/composer/submit/native-chat-structured-composer-transport.ts index 0b03de00b88..c2fa6bee462 100644 --- a/src/renderer/src/components/native-chat/composer/submit/native-chat-structured-composer-transport.ts +++ b/src/renderer/src/components/native-chat/composer/submit/native-chat-structured-composer-transport.ts @@ -27,6 +27,8 @@ export type NativeChatStructuredComposerTransport = { sendOut?: boolean /** Send is held for this reason, which the disabled button says. */ sendBlockedReason?: string + /** Typing stays open while Send is held: the empty field's placeholder says why. */ + heldHint?: string /** What a plain send does now, and whether Cmd/Ctrl+Enter would send it the other way. */ sendFollowUp?: NativeChatSendFollowUp /** `onSent` fires once a conversation command leaves for the host, before its reply. */ diff --git a/src/renderer/src/components/native-chat/composer/submit/use-native-chat-structured-composer-transport.ts b/src/renderer/src/components/native-chat/composer/submit/use-native-chat-structured-composer-transport.ts index 76ee19910b8..197973ddf40 100644 --- a/src/renderer/src/components/native-chat/composer/submit/use-native-chat-structured-composer-transport.ts +++ b/src/renderer/src/components/native-chat/composer/submit/use-native-chat-structured-composer-transport.ts @@ -85,6 +85,8 @@ export function useNativeChatStructuredComposerTransport(args: { reveal: { revealLatest: () => void; captureRevealLatest: () => () => void } /** Host reason for refusing turns and holding Send. */ sendBlockedReason?: string | null + /** END-PASS CHAT-RENEW: the empty field's placeholder while Send is held (typing stays open). */ + heldHint?: string }): NativeChatStructuredComposerTransport { const { props, controller, forks, sendThroughLaunch, starting, worktreeId, optionPickerRequest } = args @@ -92,6 +94,7 @@ export function useNativeChatStructuredComposerTransport(args: { const { revealLatest: onSubmitted, captureRevealLatest: captureAdmittedReveal } = args.reveal const { transportEnabled } = args const sendBlockedReason = args.sendBlockedReason ?? null + const { heldHint } = args const acceptsImages = useStructuredAgentAcceptsImages( props.target, props.agent, @@ -176,6 +179,7 @@ export function useNativeChatStructuredComposerTransport(args: { sendOut: controller.sendOut, sendFollowUp: controller.sendFollowUp, ...(sendBlockedReason !== null ? { sendBlockedReason } : {}), + ...(heldHint ? { heldHint } : {}), optionsSurface: controller.optionSurface, conversationCommands: controller.conversationCommands, compactInstructions: controller.compactInstructions, @@ -216,6 +220,7 @@ export function useNativeChatStructuredComposerTransport(args: { props.sessionId, props.target, sendBlockedReason, + heldHint, sendThroughLaunch, setOptionPickerRequest, starting,