Skip to content

Commit 3dae913

Browse files
committed
cli: Enhance suggest-followups styling and persist click state
- Add persistent click state per toolCallId (survives new followup sets) - Hover styling: gray background, acid green arrow/title, bold, italic description - Vertically align descriptions like slash menu - Preserve user input when clicking followups - Rename "Next steps" to "Suggested followups"
1 parent cb518fe commit 3dae913

File tree

3 files changed

+150
-140
lines changed

3 files changed

+150
-140
lines changed

cli/src/chat.tsx

Lines changed: 36 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { useEvent } from './hooks/use-event'
3232
import { useExitHandler } from './hooks/use-exit-handler'
3333
import { useInputHistory } from './hooks/use-input-history'
3434
import { useMessageQueue, type QueuedMessage } from './hooks/use-message-queue'
35+
import { usePublishMutation } from './hooks/use-publish-mutation'
3536
import { useQueueControls } from './hooks/use-queue-controls'
3637
import { useQueueUi } from './hooks/use-queue-ui'
3738
import { useChatScrollbox } from './hooks/use-scroll-management'
@@ -49,24 +50,24 @@ import { usePublishStore } from './state/publish-store'
4950
import {
5051
addClipboardPlaceholder,
5152
addPendingImageFromFile,
53+
capturePendingImages,
5254
validateAndAddImage,
5355
} from './utils/add-pending-image'
5456
import { createChatScrollAcceleration } from './utils/chat-scroll-accel'
5557
import { showClipboardMessage } from './utils/clipboard'
5658
import { readClipboardImage } from './utils/clipboard-image'
57-
import { createPasteHandler } from './utils/strings'
5859
import { getInputModeConfig } from './utils/input-modes'
5960
import {
6061
type ChatKeyboardState,
6162
createDefaultChatKeyboardState,
6263
} from './utils/keyboard-actions'
6364
import { loadLocalAgents } from './utils/local-agent-registry'
64-
import { usePublishMutation } from './hooks/use-publish-mutation'
6565
import { buildMessageTree } from './utils/message-tree-utils'
6666
import {
6767
getStatusIndicatorState,
6868
type AuthStatus,
6969
} from './utils/status-indicator-state'
70+
import { createPasteHandler } from './utils/strings'
7071
import { computeInputLayoutMetrics } from './utils/text-layout'
7172
import { createMarkdownPalette } from './utils/theme-system'
7273

@@ -148,15 +149,12 @@ export const Chat = ({
148149
agentSelectedIndex,
149150
setAgentSelectedIndex,
150151
streamingAgents,
151-
setStreamingAgents,
152152
focusedAgentId,
153153
setFocusedAgentId,
154154
messages,
155155
setMessages,
156156
activeSubagents,
157-
setActiveSubagents,
158157
isChainInProgress,
159-
setIsChainInProgress,
160158
agentMode,
161159
setAgentMode,
162160
toggleAgentMode,
@@ -176,15 +174,12 @@ export const Chat = ({
176174
agentSelectedIndex: store.agentSelectedIndex,
177175
setAgentSelectedIndex: store.setAgentSelectedIndex,
178176
streamingAgents: store.streamingAgents,
179-
setStreamingAgents: store.setStreamingAgents,
180177
focusedAgentId: store.focusedAgentId,
181178
setFocusedAgentId: store.setFocusedAgentId,
182179
messages: store.messages,
183180
setMessages: store.setMessages,
184181
activeSubagents: store.activeSubagents,
185-
setActiveSubagents: store.setActiveSubagents,
186182
isChainInProgress: store.isChainInProgress,
187-
setIsChainInProgress: store.setIsChainInProgress,
188183
agentMode: store.agentMode,
189184
setAgentMode: store.setAgentMode,
190185
toggleAgentMode: store.toggleAgentMode,
@@ -625,62 +620,51 @@ export const Chat = ({
625620
// Handle followup suggestion clicks
626621
useEffect(() => {
627622
const handleFollowupClick = (event: Event) => {
628-
const customEvent = event as CustomEvent<{ prompt: string; index: number }>
629-
const { prompt, index } = customEvent.detail
623+
const customEvent = event as CustomEvent<{
624+
prompt: string
625+
index: number
626+
toolCallId: string
627+
}>
628+
const { prompt, index, toolCallId } = customEvent.detail
630629

631-
// Mark this followup as clicked
632-
useChatStore.getState().markFollowupClicked(index)
630+
// Mark this followup as clicked (persisted per toolCallId)
631+
useChatStore.getState().markFollowupClicked(toolCallId, index)
633632

634-
// Send the followup prompt as a user message
633+
// Send the followup prompt directly without clearing the user's current input
635634
ensureQueueActiveBeforeSubmit()
636-
void routeUserPrompt({
637-
abortControllerRef,
638-
agentMode,
639-
inputRef,
640-
inputValue: prompt,
641-
isChainInProgressRef,
642-
isStreaming,
643-
logoutMutation,
644-
streamMessageIdRef,
645-
addToQueue,
646-
clearMessages,
647-
saveToHistory,
648-
scrollToLatest,
649-
sendMessage,
650-
setCanProcessQueue,
651-
setInputFocused,
652-
setInputValue,
653-
setIsAuthenticated,
654-
setMessages,
655-
setUser,
656-
stopStreaming,
657-
})
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+
}
658651
}
659652

660653
globalThis.addEventListener('codebuff:send-followup', handleFollowupClick)
661654
return () => {
662-
globalThis.removeEventListener('codebuff:send-followup', handleFollowupClick)
655+
globalThis.removeEventListener(
656+
'codebuff:send-followup',
657+
handleFollowupClick,
658+
)
663659
}
664660
}, [
665-
abortControllerRef,
666661
agentMode,
667-
inputRef,
668-
isChainInProgressRef,
669662
isStreaming,
670-
logoutMutation,
671663
streamMessageIdRef,
664+
isChainInProgressRef,
672665
addToQueue,
673-
clearMessages,
674-
saveToHistory,
675666
scrollToLatest,
676667
sendMessage,
677-
setCanProcessQueue,
678-
setInputFocused,
679-
setInputValue,
680-
setIsAuthenticated,
681-
setMessages,
682-
setUser,
683-
stopStreaming,
684668
ensureQueueActiveBeforeSubmit,
685669
])
686670

@@ -1085,14 +1069,14 @@ export const Chat = ({
10851069
onSlashMenuSelect: async () => {
10861070
const selected = slashMatches[slashSelectedIndex] || slashMatches[0]
10871071
if (!selected) return
1088-
1072+
10891073
// Execute the selected slash command immediately
10901074
const commandString = `/${selected.id}`
10911075
setSlashSelectedIndex(0)
1092-
1076+
10931077
ensureQueueActiveBeforeSubmit()
10941078
const result = await onSubmitPrompt(commandString, agentMode)
1095-
1079+
10961080
if (result?.openFeedbackMode) {
10971081
// Save the feedback text that was set by the command handler before opening feedback mode
10981082
const prefilledText = useFeedbackStore.getState().feedbackText

0 commit comments

Comments
 (0)