mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 18:37:24 +00:00
fix(cloud): CHAT-RENEW the held hint rides the structured transport into the composer send state (keeps NativeChatComposerPane under max-lines)
This commit is contained in:
+6
-3
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -217,7 +217,8 @@ export function NativeChatStructuredSession(props: Omit<NativeChatStructuredView
|
||||
setOptionPickerRequest,
|
||||
onError: reportComposerError,
|
||||
reveal: submits,
|
||||
sendBlockedReason: hostSendGate.sendBlockedReason ?? install.sendBlockedReason
|
||||
sendBlockedReason: hostSendGate.sendBlockedReason ?? install.sendBlockedReason,
|
||||
...(hostHeld || !install.heldHint ? {} : { heldHint: install.heldHint })
|
||||
})
|
||||
const continuation = useNativeChatInterruptedContinuation({
|
||||
...controller,
|
||||
@@ -393,7 +394,6 @@ export function NativeChatStructuredSession(props: Omit<NativeChatStructuredView
|
||||
agent={props.agent}
|
||||
{...stopControls.composer}
|
||||
unavailableHint={install.unavailableHint}
|
||||
heldHint={hostHeld ? undefined : install.heldHint}
|
||||
steerHead={stopControls.stopping ? undefined : submits.queuedMessages.steerHead}
|
||||
structuredTransport={transport}
|
||||
notices={notices}
|
||||
|
||||
@@ -42,7 +42,6 @@ export const NativeChatComposerPane = forwardRef<ComposerHandle, C.NativeChatCom
|
||||
agent,
|
||||
canSend = true,
|
||||
unavailableHint,
|
||||
heldHint,
|
||||
isWorking = false,
|
||||
isStopping = false,
|
||||
afterStop,
|
||||
@@ -367,7 +366,6 @@ export const NativeChatComposerPane = forwardRef<ComposerHandle, C.NativeChatCom
|
||||
hasPty={hasPty}
|
||||
canSend={canSend}
|
||||
unavailableHint={unavailableHint}
|
||||
heldHint={heldHint}
|
||||
autocomplete={autocomplete}
|
||||
mentionFiles={mentionFiles}
|
||||
activeSuggestion={activeSuggestion}
|
||||
|
||||
@@ -24,8 +24,6 @@ export type NativeChatComposerProps = {
|
||||
canSend?: boolean
|
||||
/** Disable unavailable input and show this reason as its placeholder. */
|
||||
unavailableHint?: string | undefined
|
||||
/** Input stays open but the transport holds Send: show this reason as the empty field's placeholder. */
|
||||
heldHint?: string | undefined
|
||||
/** True while the hosted TUI reports an in-flight turn; swaps Send to Stop. */
|
||||
isWorking?: boolean
|
||||
/** This client's Stop request is in flight: the Stop control is disabled and says so. */
|
||||
|
||||
+9
-1
@@ -12,6 +12,8 @@ import type { Translate } from '../../../../i18n/use-translate'
|
||||
export type NativeChatComposerSendState = {
|
||||
/** Why Send is disabled, when the user can do something about it. */
|
||||
sendBlockedReason: string | null
|
||||
/** A host hold's placeholder for the empty field (typing stays open). */
|
||||
heldHint?: string
|
||||
/** Nothing may be sent yet: an upload, a paste being saved, or the host holds it. */
|
||||
sendHeld: boolean
|
||||
/** The draft holds text or images: while the agent works, Send takes Stop's place. */
|
||||
@@ -35,7 +37,12 @@ export function nativeChatComposerSendState(
|
||||
structuredTransport:
|
||||
| Pick<
|
||||
NativeChatStructuredComposerTransport,
|
||||
'sendOut' | 'sendBlockedReason' | 'threadGoal' | 'sendFollowUp' | 'optionSnapshot'
|
||||
| 'sendOut'
|
||||
| 'sendBlockedReason'
|
||||
| 'heldHint'
|
||||
| 'threadGoal'
|
||||
| 'sendFollowUp'
|
||||
| 'optionSnapshot'
|
||||
>
|
||||
| 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,
|
||||
|
||||
+2
@@ -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. */
|
||||
|
||||
+5
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user