| ... | @@ -124,6 +124,7 @@ import { | ... | @@ -124,6 +124,7 @@ import { |
| 124 | import { ComposerStashBadge } from "./ComposerStashBadge"; | 124 | import { ComposerStashBadge } from "./ComposerStashBadge"; |
| 125 | import { ComposerStashMenu } from "./ComposerStashMenu"; | 125 | import { ComposerStashMenu } from "./ComposerStashMenu"; |
| 126 | import { useComposerMenuState } from "./useComposerMenuState"; | 126 | import { useComposerMenuState } from "./useComposerMenuState"; |
| | 127 | import { useComposerTriggerState } from "./useComposerTriggerState"; |
| 127 | import { useComposerFocusState } from "./useComposerFocusState"; | 128 | import { useComposerFocusState } from "./useComposerFocusState"; |
| 128 | import { useComposerMultilinePrompt } from "./useComposerMultilinePrompt"; | 129 | import { useComposerMultilinePrompt } from "./useComposerMultilinePrompt"; |
| 129 | import { | 130 | import { |
| ... | @@ -2065,9 +2066,13 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -2065,9 +2066,13 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 2065 | const [composerCursor, setComposerCursor] = useState(() => | 2066 | const [composerCursor, setComposerCursor] = useState(() => |
| 2066 | collapseExpandedComposerCursor(prompt, prompt.length), | 2067 | collapseExpandedComposerCursor(prompt, prompt.length), |
| 2067 | ); | 2068 | ); |
| 2068 | const [composerTrigger, setComposerTrigger] = useState<ComposerTrigger | null>(() => | 2069 | const { |
| 2069 | detectComposerTrigger(prompt, prompt.length), | 2070 | trigger: composerTrigger, |
| 2070 | ); | 2071 | setTrigger: setComposerTrigger, |
| | 2072 | resolveTrigger: resolveComposerTrigger, |
| | 2073 | dismissTrigger: dismissComposerTrigger, |
| | 2074 | resetTrigger: resetComposerTrigger, |
| | 2075 | } = useComposerTriggerState(() => detectComposerTrigger(prompt, prompt.length)); |
| 2071 | const [composerHighlightedItemId, setComposerHighlightedItemId] = useState<string | null>(null); | 2076 | const [composerHighlightedItemId, setComposerHighlightedItemId] = useState<string | null>(null); |
| 2072 | // Active ArrowUp recall. Cleared on edit and on thread switch. | 2077 | // Active ArrowUp recall. Cleared on edit and on thread switch. |
| 2073 | const promptHistoryPositionRef = useRef<ComposerPromptHistoryPosition | null>(null); | 2078 | const promptHistoryPositionRef = useRef<ComposerPromptHistoryPosition | null>(null); |
| ... | @@ -2586,7 +2591,13 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -2586,7 +2591,13 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 2586 | setComposerTrigger(detectComposerTrigger(nextPrompt, nextPrompt.length)); | 2591 | setComposerTrigger(detectComposerTrigger(nextPrompt, nextPrompt.length)); |
| 2587 | scheduleComposerFocus(); | 2592 | scheduleComposerFocus(); |
| 2588 | }, | 2593 | }, |
| 2589 | [composerDraftTarget, promptRef, scheduleComposerFocus, setComposerDraftPrompt], | 2594 | [ |
| | 2595 | composerDraftTarget, |
| | 2596 | promptRef, |
| | 2597 | scheduleComposerFocus, |
| | 2598 | setComposerDraftPrompt, |
| | 2599 | setComposerTrigger, |
| | 2600 | ], |
| 2590 | ); | 2601 | ); |
| 2591 | | 2602 | |
| 2592 | const providerTraitsMenuContent = renderProviderTraitsMenuContent({ | 2603 | const providerTraitsMenuContent = renderProviderTraitsMenuContent({ |
| ... | @@ -3090,7 +3101,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -3090,7 +3101,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 3090 | promptRef.current = prompt; | 3101 | promptRef.current = prompt; |
| 3091 | const { cursor, trigger } = composerStateAtPromptEnd(prompt); | 3102 | const { cursor, trigger } = composerStateAtPromptEnd(prompt); |
| 3092 | setComposerCursor(cursor); | 3103 | setComposerCursor(cursor); |
| 3093 | setComposerTrigger(trigger); | 3104 | resetComposerTrigger(trigger); |
| 3094 | } | 3105 | } |
| 3095 | lastSyncedPendingInputRef.current = null; | 3106 | lastSyncedPendingInputRef.current = null; |
| 3096 | return; | 3107 | return; |
| ... | @@ -3115,7 +3126,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -3115,7 +3126,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 3115 | promptRef.current = nextCustomAnswer; | 3126 | promptRef.current = nextCustomAnswer; |
| 3116 | const { cursor, trigger } = composerStateAtPromptEnd(nextCustomAnswer); | 3127 | const { cursor, trigger } = composerStateAtPromptEnd(nextCustomAnswer); |
| 3117 | setComposerCursor(cursor); | 3128 | setComposerCursor(cursor); |
| 3118 | setComposerTrigger(trigger); | 3129 | resetComposerTrigger(trigger); |
| 3119 | setComposerHighlightedItemId(null); | 3130 | setComposerHighlightedItemId(null); |
| 3120 | }, [ | 3131 | }, [ |
| 3121 | activePendingProgress?.customAnswer, | 3132 | activePendingProgress?.customAnswer, |
| ... | @@ -3123,6 +3134,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -3123,6 +3134,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 3123 | activePendingUserInput?.requestId, | 3134 | activePendingUserInput?.requestId, |
| 3124 | prompt, | 3135 | prompt, |
| 3125 | promptRef, | 3136 | promptRef, |
| | 3137 | resetComposerTrigger, |
| 3126 | ]); | 3138 | ]); |
| 3127 | | 3139 | |
| 3128 | // ------------------------------------------------------------------ | 3140 | // ------------------------------------------------------------------ |
| ... | @@ -3133,10 +3145,10 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -3133,10 +3145,10 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 3133 | setComposerSubmissionError(null); | 3145 | setComposerSubmissionError(null); |
| 3134 | setProviderInputSubmissionError(null); | 3146 | setProviderInputSubmissionError(null); |
| 3135 | setComposerCursor(collapseExpandedComposerCursor(promptRef.current, promptRef.current.length)); | 3147 | setComposerCursor(collapseExpandedComposerCursor(promptRef.current, promptRef.current.length)); |
| 3136 | setComposerTrigger(detectComposerTrigger(promptRef.current, promptRef.current.length)); | 3148 | resetComposerTrigger(detectComposerTrigger(promptRef.current, promptRef.current.length)); |
| 3137 | setIsDragOverComposer(false); | 3149 | setIsDragOverComposer(false); |
| 3138 | setIsComposerScrollCollapsed(false); | 3150 | setIsComposerScrollCollapsed(false); |
| 3139 | }, [draftId, activeThreadId, promptRef, setIsComposerScrollCollapsed]); | 3151 | }, [draftId, activeThreadId, promptRef, resetComposerTrigger, setIsComposerScrollCollapsed]); |
| 3140 | | 3152 | |
| 3141 | // ------------------------------------------------------------------ | 3153 | // ------------------------------------------------------------------ |
| 3142 | // Footer compact layout observation | 3154 | // Footer compact layout observation |
| ... | @@ -3419,6 +3431,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -3419,6 +3431,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 3419 | onChangeActivePendingUserInputCustomAnswer, | 3431 | onChangeActivePendingUserInputCustomAnswer, |
| 3420 | promptRef, | 3432 | promptRef, |
| 3421 | setPrompt, | 3433 | setPrompt, |
| | 3434 | setComposerTrigger, |
| 3422 | composerDraftTarget, | 3435 | composerDraftTarget, |
| 3423 | composerTerminalContexts, | 3436 | composerTerminalContexts, |
| 3424 | setComposerDraftTerminalContexts, | 3437 | setComposerDraftTerminalContexts, |
| ... | @@ -3514,6 +3527,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -3514,6 +3527,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 3514 | onChangeActivePendingUserInputCustomAnswer, | 3527 | onChangeActivePendingUserInputCustomAnswer, |
| 3515 | promptRef, | 3528 | promptRef, |
| 3516 | setPrompt, | 3529 | setPrompt, |
| | 3530 | setComposerTrigger, |
| 3517 | ], | 3531 | ], |
| 3518 | ); | 3532 | ); |
| 3519 | | 3533 | |
| ... | @@ -3542,9 +3556,11 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -3542,9 +3556,11 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 3542 | const snapshot = readComposerSnapshot(); | 3556 | const snapshot = readComposerSnapshot(); |
| 3543 | return { | 3557 | return { |
| 3544 | snapshot, | 3558 | snapshot, |
| 3545 | trigger: detectComposerTrigger(snapshot.value, snapshot.expandedCursor), | 3559 | trigger: resolveComposerTrigger( |
| | 3560 | detectComposerTrigger(snapshot.value, snapshot.expandedCursor), |
| | 3561 | ), |
| 3546 | }; | 3562 | }; |
| 3547 | }, [readComposerSnapshot]); | 3563 | }, [readComposerSnapshot, resolveComposerTrigger]); |
| 3548 | | 3564 | |
| 3549 | const { onUsageLimitsCommand } = props; | 3565 | const { onUsageLimitsCommand } = props; |
| 3550 | const onSelectComposerItem = useCallback( | 3566 | const onSelectComposerItem = useCallback( |
| ... | @@ -3897,7 +3913,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -3897,7 +3913,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 3897 | setComposerTrigger(null); | 3913 | setComposerTrigger(null); |
| 3898 | setComposerHighlightedItemId(null); | 3914 | setComposerHighlightedItemId(null); |
| 3899 | }, | 3915 | }, |
| 3900 | [composerDraftTarget, promptRef, setComposerDraftPrompt], | 3916 | [composerDraftTarget, promptRef, setComposerDraftPrompt, setComposerTrigger], |
| 3901 | ); | 3917 | ); |
| 3902 | | 3918 | |
| 3903 | const navigatePromptHistory = useCallback( | 3919 | const navigatePromptHistory = useCallback( |
| ... | @@ -3956,7 +3972,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -3956,7 +3972,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 3956 | // Callbacks: command key | 3972 | // Callbacks: command key |
| 3957 | // ------------------------------------------------------------------ | 3973 | // ------------------------------------------------------------------ |
| 3958 | const onComposerCommandKey = ( | 3974 | const onComposerCommandKey = ( |
| 3959 | key: "ArrowDown" | "ArrowUp" | "Enter" | "Tab", | 3975 | key: "ArrowDown" | "ArrowUp" | "Enter" | "Tab" | "Escape", |
| 3960 | event: KeyboardEvent, | 3976 | event: KeyboardEvent, |
| 3961 | isTaskItem = false, | 3977 | isTaskItem = false, |
| 3962 | ) => { | 3978 | ) => { |
| ... | @@ -3967,6 +3983,12 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -3967,6 +3983,12 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 3967 | } | 3983 | } |
| 3968 | const { trigger } = resolveActiveComposerTrigger(); | 3984 | const { trigger } = resolveActiveComposerTrigger(); |
| 3969 | const menuIsActive = composerMenuOpenRef.current || trigger !== null; | 3985 | const menuIsActive = composerMenuOpenRef.current || trigger !== null; |
| | 3986 | if (key === "Escape") { |
| | 3987 | if (!menuIsActive || event.isComposing || event.keyCode === 229) return false; |
| | 3988 | dismissComposerTrigger(trigger); |
| | 3989 | composerMenuOpenRef.current = false; |
| | 3990 | return true; |
| | 3991 | } |
| 3970 | if (menuIsActive) { | 3992 | if (menuIsActive) { |
| 3971 | const currentItems = composerMenuItemsRef.current; | 3993 | const currentItems = composerMenuItemsRef.current; |
| 3972 | const selectedItem = activeComposerMenuItemRef.current ?? currentItems[0]; | 3994 | const selectedItem = activeComposerMenuItemRef.current ?? currentItems[0]; |
| ... | @@ -4356,6 +4378,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -4356,6 +4378,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 4356 | environmentId, | 4378 | environmentId, |
| 4357 | promptRef, | 4379 | promptRef, |
| 4358 | setComposerDraftPrompt, | 4380 | setComposerDraftPrompt, |
| | 4381 | setComposerTrigger, |
| 4359 | takeStashEntry, | 4382 | takeStashEntry, |
| 4360 | importContextRecords, | 4383 | importContextRecords, |
| 4361 | ], | 4384 | ], |
| ... | @@ -4619,6 +4642,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -4619,6 +4642,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 4619 | clearComposerDraftPromptAndImages, | 4642 | clearComposerDraftPromptAndImages, |
| 4620 | clearComposerDraftTerminalContexts, | 4643 | clearComposerDraftTerminalContexts, |
| 4621 | setComposerDraftPrompt, | 4644 | setComposerDraftPrompt, |
| | 4645 | setComposerTrigger, |
| 4622 | composerDraftTarget, | 4646 | composerDraftTarget, |
| 4623 | composerFilesRef, | 4647 | composerFilesRef, |
| 4624 | composerImagesRef, | 4648 | composerImagesRef, |
| ... | @@ -5932,7 +5956,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -5932,7 +5956,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 5932 | const cursor = clampCollapsedComposerCursor(promptForState, options?.cursor ?? 0); | 5956 | const cursor = clampCollapsedComposerCursor(promptForState, options?.cursor ?? 0); |
| 5933 | setComposerHighlightedItemId(null); | 5957 | setComposerHighlightedItemId(null); |
| 5934 | setComposerCursor(cursor); | 5958 | setComposerCursor(cursor); |
| 5935 | setComposerTrigger( | 5959 | resetComposerTrigger( |
| 5936 | options?.detectTrigger | 5960 | options?.detectTrigger |
| 5937 | ? detectComposerTrigger( | 5961 | ? detectComposerTrigger( |
| 5938 | promptForState, | 5962 | promptForState, |
| ... | @@ -6041,6 +6065,8 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -6041,6 +6065,8 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 6041 | isComposerModelPickerOpen, | 6065 | isComposerModelPickerOpen, |
| 6042 | openModelPicker, | 6066 | openModelPicker, |
| 6043 | readComposerSnapshot, | 6067 | readComposerSnapshot, |
| | 6068 | resetComposerTrigger, |
| | 6069 | setComposerTrigger, |
| 6044 | selectedModel, | 6070 | selectedModel, |
| 6045 | selectedModelOptionsForDispatch, | 6071 | selectedModelOptionsForDispatch, |
| 6046 | selectedModelSelection, | 6072 | selectedModelSelection, |