Skip to content

Commit 0519cff

Browse files
committed
cli: Clean up StatusBar props, pause timer during ask-user
1 parent 9963b4c commit 0519cff

File tree

4 files changed

+113
-270
lines changed

4 files changed

+113
-270
lines changed

cli/src/chat.tsx

Lines changed: 93 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import { useShallow } from 'zustand/react/shallow'
1212

1313
import { routeUserPrompt, addBashMessageToHistory } from './commands/router'
14+
import type { CommandResult } from './commands/command-registry'
1415
import { AnnouncementBanner } from './components/announcement-banner'
1516
import { ChatInputBar } from './components/chat-input-bar'
1617
import { MessageWithAgents } from './components/message-with-agents'
@@ -50,7 +51,6 @@ import { usePublishStore } from './state/publish-store'
5051
import {
5152
addClipboardPlaceholder,
5253
addPendingImageFromFile,
53-
capturePendingImages,
5454
validateAndAddImage,
5555
} from './utils/add-pending-image'
5656
import { createChatScrollAcceleration } from './utils/chat-scroll-accel'
@@ -617,6 +617,61 @@ export const Chat = ({
617617

618618
sendMessageRef.current = sendMessage
619619

620+
const onSubmitPrompt = useEvent(
621+
async (
622+
content: string,
623+
mode: AgentMode,
624+
options?: { preserveInputValue?: boolean },
625+
) => {
626+
ensureQueueActiveBeforeSubmit()
627+
628+
const previousInputValue =
629+
options?.preserveInputValue === true
630+
? (() => {
631+
const {
632+
inputValue: text,
633+
cursorPosition,
634+
lastEditDueToNav,
635+
} = useChatStore.getState()
636+
return { text, cursorPosition, lastEditDueToNav }
637+
})()
638+
: null
639+
640+
const result = await routeUserPrompt({
641+
abortControllerRef,
642+
agentMode: mode,
643+
inputRef,
644+
inputValue: content,
645+
isChainInProgressRef,
646+
isStreaming,
647+
logoutMutation,
648+
streamMessageIdRef,
649+
addToQueue,
650+
clearMessages,
651+
saveToHistory,
652+
scrollToLatest,
653+
sendMessage,
654+
setCanProcessQueue,
655+
setInputFocused,
656+
setInputValue,
657+
setIsAuthenticated,
658+
setMessages,
659+
setUser,
660+
stopStreaming,
661+
})
662+
663+
if (previousInputValue) {
664+
setInputValue({
665+
text: previousInputValue.text,
666+
cursorPosition: previousInputValue.cursorPosition,
667+
lastEditDueToNav: previousInputValue.lastEditDueToNav,
668+
})
669+
}
670+
671+
return result
672+
},
673+
)
674+
620675
// Handle followup suggestion clicks
621676
useEffect(() => {
622677
const handleFollowupClick = (event: Event) => {
@@ -630,24 +685,8 @@ export const Chat = ({
630685
// Mark this followup as clicked (persisted per toolCallId)
631686
useChatStore.getState().markFollowupClicked(toolCallId, index)
632687

633-
// Send the followup prompt directly without clearing the user's current input
634-
ensureQueueActiveBeforeSubmit()
635-
636-
if (
637-
isStreaming ||
638-
streamMessageIdRef.current ||
639-
isChainInProgressRef.current
640-
) {
641-
const pendingImagesForQueue = capturePendingImages()
642-
// Queue the followup message
643-
addToQueue(prompt, pendingImagesForQueue)
644-
} else {
645-
// Send directly
646-
sendMessage({ content: prompt, agentMode })
647-
setTimeout(() => {
648-
scrollToLatest()
649-
}, 0)
650-
}
688+
// Send the followup prompt while preserving any text the user has typed
689+
void onSubmitPrompt(prompt, agentMode, { preserveInputValue: true })
651690
}
652691

653692
globalThis.addEventListener('codebuff:send-followup', handleFollowupClick)
@@ -659,40 +698,9 @@ export const Chat = ({
659698
}
660699
}, [
661700
agentMode,
662-
isStreaming,
663-
streamMessageIdRef,
664-
isChainInProgressRef,
665-
addToQueue,
666-
scrollToLatest,
667-
sendMessage,
668-
ensureQueueActiveBeforeSubmit,
701+
onSubmitPrompt,
669702
])
670703

671-
const onSubmitPrompt = useEvent((content: string, mode: AgentMode) => {
672-
return routeUserPrompt({
673-
abortControllerRef,
674-
agentMode: mode,
675-
inputRef,
676-
inputValue: content,
677-
isChainInProgressRef,
678-
isStreaming,
679-
logoutMutation,
680-
streamMessageIdRef,
681-
addToQueue,
682-
clearMessages,
683-
saveToHistory,
684-
scrollToLatest,
685-
sendMessage,
686-
setCanProcessQueue,
687-
setInputFocused,
688-
setInputValue,
689-
setIsAuthenticated,
690-
setMessages,
691-
setUser,
692-
stopStreaming,
693-
})
694-
})
695-
696704
// handleSlashItemClick is defined later after feedback/publish stores are available
697705

698706
const handleMentionItemClick = useCallback(
@@ -772,32 +780,23 @@ export const Chat = ({
772780

773781
const publishMutation = usePublishMutation()
774782

775-
// Click handler for slash menu items - executes command immediately
776-
const handleSlashItemClick = useCallback(
777-
async (index: number) => {
778-
const selected = slashMatches[index]
779-
if (!selected) return
780-
781-
// Execute the selected slash command immediately
782-
const commandString = `/${selected.id}`
783-
setSlashSelectedIndex(0)
784-
785-
ensureQueueActiveBeforeSubmit()
786-
const result = await onSubmitPrompt(commandString, agentMode)
783+
const handleCommandResult = useCallback(
784+
(result?: CommandResult) => {
785+
if (!result) return
787786

788-
if (result?.openFeedbackMode) {
787+
if (result.openFeedbackMode) {
789788
// Save the feedback text that was set by the command handler before opening feedback mode
790-
const prefilledText = useFeedbackStore.getState().feedbackText
791-
const prefilledCursor = useFeedbackStore.getState().feedbackCursor
789+
const { feedbackText, feedbackCursor } = useFeedbackStore.getState()
792790
saveCurrentInput('', 0)
793791
openFeedbackForMessage(null)
794792
// Restore the prefilled text after openFeedbackForMessage resets it
795-
if (prefilledText) {
796-
useFeedbackStore.getState().setFeedbackText(prefilledText)
797-
useFeedbackStore.getState().setFeedbackCursor(prefilledCursor)
793+
if (feedbackText) {
794+
useFeedbackStore.getState().setFeedbackText(feedbackText)
795+
useFeedbackStore.getState().setFeedbackCursor(feedbackCursor)
798796
}
799797
}
800-
if (result?.openPublishMode) {
798+
799+
if (result.openPublishMode) {
801800
if (result.preSelectAgents && result.preSelectAgents.length > 0) {
802801
// preSelectAgents already sets publishMode: true, so don't call openPublishMode
803802
// which would reset the selectedAgentIds
@@ -807,16 +806,28 @@ export const Chat = ({
807806
}
808807
}
809808
},
809+
[saveCurrentInput, openFeedbackForMessage, openPublishMode, preSelectAgents],
810+
)
811+
812+
// Click handler for slash menu items - executes command immediately
813+
const handleSlashItemClick = useCallback(
814+
async (index: number) => {
815+
const selected = slashMatches[index]
816+
if (!selected) return
817+
818+
// Execute the selected slash command immediately
819+
const commandString = `/${selected.id}`
820+
setSlashSelectedIndex(0)
821+
822+
const result = await onSubmitPrompt(commandString, agentMode)
823+
handleCommandResult(result)
824+
},
810825
[
811826
slashMatches,
812827
setSlashSelectedIndex,
813-
ensureQueueActiveBeforeSubmit,
814828
onSubmitPrompt,
815829
agentMode,
816-
saveCurrentInput,
817-
openFeedbackForMessage,
818-
openPublishMode,
819-
preSelectAgents,
830+
handleCommandResult,
820831
],
821832
)
822833

@@ -897,79 +908,13 @@ export const Chat = ({
897908
}, [feedbackMode, askUserState, inputRef])
898909

899910
const handleSubmit = useCallback(async () => {
900-
ensureQueueActiveBeforeSubmit()
901-
902-
const result = await routeUserPrompt({
903-
abortControllerRef,
904-
agentMode,
905-
inputRef,
906-
inputValue,
907-
isChainInProgressRef,
908-
isStreaming,
909-
logoutMutation,
910-
streamMessageIdRef,
911-
addToQueue,
912-
clearMessages,
913-
saveToHistory,
914-
scrollToLatest,
915-
sendMessage,
916-
setCanProcessQueue,
917-
setInputFocused,
918-
setInputValue,
919-
setIsAuthenticated,
920-
setMessages,
921-
setUser,
922-
stopStreaming,
923-
})
924-
925-
if (result?.openFeedbackMode) {
926-
// Save the feedback text that was set by the command handler before opening feedback mode
927-
const prefilledText = useFeedbackStore.getState().feedbackText
928-
const prefilledCursor = useFeedbackStore.getState().feedbackCursor
929-
saveCurrentInput('', 0)
930-
openFeedbackForMessage(null)
931-
// Restore the prefilled text after openFeedbackForMessage resets it
932-
if (prefilledText) {
933-
useFeedbackStore.getState().setFeedbackText(prefilledText)
934-
useFeedbackStore.getState().setFeedbackCursor(prefilledCursor)
935-
}
936-
}
937-
938-
if (result?.openPublishMode) {
939-
if (result.preSelectAgents && result.preSelectAgents.length > 0) {
940-
// preSelectAgents already sets publishMode: true, so don't call openPublishMode
941-
// which would reset the selectedAgentIds
942-
preSelectAgents(result.preSelectAgents)
943-
} else {
944-
openPublishMode()
945-
}
946-
}
911+
const result = await onSubmitPrompt(inputValue, agentMode)
912+
handleCommandResult(result)
947913
}, [
948-
abortControllerRef,
949-
agentMode,
950-
inputRef,
914+
onSubmitPrompt,
951915
inputValue,
952-
isChainInProgressRef,
953-
isStreaming,
954-
logoutMutation,
955-
streamMessageIdRef,
956-
addToQueue,
957-
clearMessages,
958-
saveToHistory,
959-
scrollToLatest,
960-
sendMessage,
961-
setCanProcessQueue,
962-
setInputFocused,
963-
setInputValue,
964-
setIsAuthenticated,
965-
setMessages,
966-
setUser,
967-
stopStreaming,
968-
ensureQueueActiveBeforeSubmit,
969-
saveCurrentInput,
970-
openFeedbackForMessage,
971-
openPublishMode,
972-
preSelectAgents,
916+
agentMode,
917+
handleCommandResult,
973918
])
974919

975920
const totalMentionMatches = agentMatches.length + fileMatches.length
@@ -1074,30 +1019,9 @@ export const Chat = ({
10741019
const commandString = `/${selected.id}`
10751020
setSlashSelectedIndex(0)
10761021

1077-
ensureQueueActiveBeforeSubmit()
10781022
const result = await onSubmitPrompt(commandString, agentMode)
10791023

1080-
if (result?.openFeedbackMode) {
1081-
// Save the feedback text that was set by the command handler before opening feedback mode
1082-
const prefilledText = useFeedbackStore.getState().feedbackText
1083-
const prefilledCursor = useFeedbackStore.getState().feedbackCursor
1084-
saveCurrentInput('', 0)
1085-
openFeedbackForMessage(null)
1086-
// Restore the prefilled text after openFeedbackForMessage resets it
1087-
if (prefilledText) {
1088-
useFeedbackStore.getState().setFeedbackText(prefilledText)
1089-
useFeedbackStore.getState().setFeedbackCursor(prefilledCursor)
1090-
}
1091-
}
1092-
if (result?.openPublishMode) {
1093-
if (result.preSelectAgents && result.preSelectAgents.length > 0) {
1094-
// preSelectAgents already sets publishMode: true, so don't call openPublishMode
1095-
// which would reset the selectedAgentIds
1096-
preSelectAgents(result.preSelectAgents)
1097-
} else {
1098-
openPublishMode()
1099-
}
1100-
}
1024+
handleCommandResult(result)
11011025
},
11021026
onSlashMenuComplete: () => {
11031027
// Complete the word without executing - same as clicking on the item
@@ -1258,13 +1182,9 @@ export const Chat = ({
12581182
setSlashSelectedIndex,
12591183
slashMatches,
12601184
slashSelectedIndex,
1261-
ensureQueueActiveBeforeSubmit,
12621185
onSubmitPrompt,
12631186
agentMode,
1264-
saveCurrentInput,
1265-
openFeedbackForMessage,
1266-
openPublishMode,
1267-
preSelectAgents,
1187+
handleCommandResult,
12681188
setAgentSelectedIndex,
12691189
agentMatches,
12701190
fileMatches,

cli/src/components/chat-input-bar.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export const ChatInputBar = ({
113113
const modeConfig = getInputModeConfig(inputMode)
114114
const askUserState = useChatStore((state) => state.askUserState)
115115
const hasAnyPreview = hasSuggestionMenu
116-
const { submitAnswers } = useAskUserBridge()
116+
const { submitAnswers, skip } = useAskUserBridge()
117117
const [askUserTitle] = React.useState(' Some questions for you ')
118118

119119
// Shared key intercept handler for suggestion menu navigation
@@ -258,11 +258,7 @@ export const ChatInputBar = ({
258258

259259
const handleFormSkip = () => {
260260
if (!askUserState) return
261-
// Submit with all questions skipped
262-
const skippedAnswers = askUserState.questions.map((_, idx) => ({
263-
questionIndex: idx,
264-
}))
265-
submitAnswers(skippedAnswers)
261+
skip()
266262
}
267263

268264
const effectivePlaceholder =

0 commit comments

Comments
 (0)